@metamask/snaps-controllers 4.0.0 → 4.1.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 +12 -1
- package/dist/cjs/services/ProxyPostMessageStream.js +3 -10
- package/dist/cjs/services/ProxyPostMessageStream.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/snaps/SnapController.js +198 -93
- package/dist/cjs/snaps/SnapController.js.map +1 -1
- package/dist/cjs/snaps/endowments/enum.js +1 -0
- package/dist/cjs/snaps/endowments/enum.js.map +1 -1
- package/dist/cjs/snaps/endowments/index.js +12 -4
- package/dist/cjs/snaps/endowments/index.js.map +1 -1
- package/dist/cjs/snaps/endowments/signature-insight.js +106 -0
- package/dist/cjs/snaps/endowments/signature-insight.js.map +1 -0
- package/dist/cjs/utils.js +32 -0
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/services/ProxyPostMessageStream.js +3 -10
- package/dist/esm/services/ProxyPostMessageStream.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/snaps/SnapController.js +200 -95
- package/dist/esm/snaps/SnapController.js.map +1 -1
- package/dist/esm/snaps/endowments/enum.js +1 -0
- package/dist/esm/snaps/endowments/enum.js.map +1 -1
- package/dist/esm/snaps/endowments/index.js +10 -4
- package/dist/esm/snaps/endowments/index.js.map +1 -1
- package/dist/esm/snaps/endowments/signature-insight.js +99 -0
- package/dist/esm/snaps/endowments/signature-insight.js.map +1 -0
- package/dist/esm/utils.js +37 -0
- package/dist/esm/utils.js.map +1 -1
- package/dist/types/services/ProxyPostMessageStream.d.ts +1 -2
- package/dist/types/services/offscreen/OffscreenExecutionService.d.ts +5 -22
- package/dist/types/services/proxy/ProxyExecutionService.d.ts +39 -0
- package/dist/types/snaps/SnapController.d.ts +31 -3
- package/dist/types/snaps/endowments/enum.d.ts +1 -0
- package/dist/types/snaps/endowments/index.d.ts +12 -0
- package/dist/types/snaps/endowments/signature-insight.d.ts +39 -0
- package/dist/types/utils.d.ts +99 -0
- package/package.json +8 -8
|
@@ -66,13 +66,13 @@ import { SubjectType } from '@metamask/permission-controller';
|
|
|
66
66
|
import { rpcErrors } from '@metamask/rpc-errors';
|
|
67
67
|
import { WALLET_SNAP_PERMISSION_KEY } 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, resolveVersionRange, SnapCaveatType, SnapStatus, SnapStatusEvents,
|
|
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';
|
|
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 {
|
|
75
|
+
import { fetchSnap, hasTimedOut, setDiff, withTimeout } from '../utils';
|
|
76
76
|
import { handlerEndowments, SnapEndowments } from './endowments';
|
|
77
77
|
import { getKeyringCaveatOrigins } from './endowments/keyring';
|
|
78
78
|
import { getRpcCaveatOrigins } from './endowments/rpc';
|
|
@@ -125,7 +125,7 @@ var _closeAllConnections = /*#__PURE__*/ new WeakMap(), _dynamicPermissions = /*
|
|
|
125
125
|
_initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
126
126
|
* Constructor helper for registering the controller's messaging system
|
|
127
127
|
* actions.
|
|
128
|
-
*/ _registerMessageHandlers = /*#__PURE__*/ new WeakSet(), _pollForLastRequestStatus = /*#__PURE__*/ new WeakSet(), _blockSnap = /*#__PURE__*/ new WeakSet(), /**
|
|
128
|
+
*/ _registerMessageHandlers = /*#__PURE__*/ new WeakSet(), _handlePreinstalledSnaps = /*#__PURE__*/ new WeakSet(), _pollForLastRequestStatus = /*#__PURE__*/ new WeakSet(), _blockSnap = /*#__PURE__*/ new WeakSet(), /**
|
|
129
129
|
* Unblocks a snap so that it can be enabled and started again. Emits
|
|
130
130
|
* {@link SnapUnblocked}. Does nothing if the snap is not installed or already
|
|
131
131
|
* unblocked.
|
|
@@ -142,7 +142,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
142
142
|
*
|
|
143
143
|
* @param snapId - The id of the snap to transition.
|
|
144
144
|
* @param event - The event enum to use to transition.
|
|
145
|
-
*/ _transition = /*#__PURE__*/ new WeakSet(), _terminateSnap = /*#__PURE__*/ new WeakSet(), /**
|
|
145
|
+
*/ _transition = /*#__PURE__*/ new WeakSet(), _terminateSnap = /*#__PURE__*/ new WeakSet(), _handleInitialConnections = /*#__PURE__*/ new WeakSet(), _addSnapToSubject = /*#__PURE__*/ new WeakSet(), /**
|
|
146
146
|
* Removes a snap's permission (caveat) from all subjects.
|
|
147
147
|
*
|
|
148
148
|
* @param snapId - The id of the Snap.
|
|
@@ -163,7 +163,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
163
163
|
*
|
|
164
164
|
* @param args - The add snap args.
|
|
165
165
|
* @returns The resulting snap object.
|
|
166
|
-
*/ _set = /*#__PURE__*/ new WeakSet(),
|
|
166
|
+
*/ _set = /*#__PURE__*/ new WeakSet(), _validateSnapPermissions = /*#__PURE__*/ new WeakSet(), /**
|
|
167
167
|
* Gets the RPC message handler for the given snap.
|
|
168
168
|
*
|
|
169
169
|
* @param snapId - The id of the Snap whose message handler to get.
|
|
@@ -181,6 +181,16 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
181
181
|
* @throws {@link Error}. If the snap exists before creation or if creation fails.
|
|
182
182
|
* @returns A `RollbackSnapshot`.
|
|
183
183
|
*/ _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(), /**
|
|
184
|
+
* Updates the permissions for a snap following an install, update or rollback.
|
|
185
|
+
*
|
|
186
|
+
* Grants newly requested permissions and revokes unused/revoked permissions.
|
|
187
|
+
*
|
|
188
|
+
* @param args - An options bag.
|
|
189
|
+
* @param args.snapId - The snap ID.
|
|
190
|
+
* @param args.newPermissions - New permissions to be granted.
|
|
191
|
+
* @param args.unusedPermissions - Unused permissions to be revoked.
|
|
192
|
+
* @param args.requestData - Optional request data from an approval.
|
|
193
|
+
*/ _updatePermissions = /*#__PURE__*/ new WeakSet(), /**
|
|
184
194
|
* Checks if a snap will pass version validation checks
|
|
185
195
|
* with the new version range that is requested. The first
|
|
186
196
|
* check that is done is to check if the existing snap version
|
|
@@ -471,6 +481,10 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
471
481
|
if (!Array.isArray(snapIds)) {
|
|
472
482
|
throw new Error('Expected array of snap ids.');
|
|
473
483
|
}
|
|
484
|
+
snapIds.forEach((snapId)=>{
|
|
485
|
+
const snap = this.getExpect(snapId);
|
|
486
|
+
assert(snap.removable !== false, `${snapId} is not removable.`);
|
|
487
|
+
});
|
|
474
488
|
await Promise.all(snapIds.map(async (snapId)=>{
|
|
475
489
|
const snap = this.getExpect(snapId);
|
|
476
490
|
const truncated = this.getTruncatedExpect(snapId);
|
|
@@ -664,6 +678,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
664
678
|
snapId,
|
|
665
679
|
type: SNAP_APPROVAL_INSTALL
|
|
666
680
|
});
|
|
681
|
+
this.messagingSystem.publish('SnapController:snapInstallStarted', snapId, origin, false);
|
|
667
682
|
// Existing snaps must be stopped before overwriting
|
|
668
683
|
if (existingSnap && this.isRunning(snapId)) {
|
|
669
684
|
await this.stopSnap(snapId, SnapStatusEvents.Stop);
|
|
@@ -697,11 +712,13 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
697
712
|
return truncated;
|
|
698
713
|
} catch (error) {
|
|
699
714
|
logError(`Error when adding ${snapId}.`, error);
|
|
715
|
+
const errorString = error instanceof Error ? error.message : error.toString();
|
|
700
716
|
_class_private_method_get(this, _updateApproval, updateApproval).call(this, pendingApproval.id, {
|
|
701
717
|
loading: false,
|
|
702
718
|
type: SNAP_APPROVAL_INSTALL,
|
|
703
|
-
error:
|
|
719
|
+
error: errorString
|
|
704
720
|
});
|
|
721
|
+
this.messagingSystem.publish('SnapController:snapInstallFailed', snapId, origin, false, errorString);
|
|
705
722
|
throw error;
|
|
706
723
|
}
|
|
707
724
|
}
|
|
@@ -733,9 +750,11 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
733
750
|
type: SNAP_APPROVAL_UPDATE
|
|
734
751
|
});
|
|
735
752
|
try {
|
|
753
|
+
this.messagingSystem.publish('SnapController:snapInstallStarted', snapId, origin, true);
|
|
736
754
|
const snap = this.getExpect(snapId);
|
|
737
|
-
const
|
|
738
|
-
const
|
|
755
|
+
const oldManifest = snap.manifest;
|
|
756
|
+
const newSnap = await fetchSnap(snapId, location);
|
|
757
|
+
const { sourceCode: sourceCodeFile, manifest: manifestFile } = newSnap;
|
|
739
758
|
const manifest = manifestFile.result;
|
|
740
759
|
const newVersion = manifest.version;
|
|
741
760
|
if (!gtVersion(newVersion, snap.version)) {
|
|
@@ -772,28 +791,22 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
772
791
|
_class_private_method_get(this, _set, set).call(this, {
|
|
773
792
|
origin,
|
|
774
793
|
id: snapId,
|
|
775
|
-
files: newSnap
|
|
794
|
+
files: newSnap,
|
|
776
795
|
isUpdate: true
|
|
777
796
|
});
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
}
|
|
784
|
-
if (
|
|
785
|
-
this.
|
|
786
|
-
approvedPermissions: approvedNewPermissions,
|
|
787
|
-
subject: {
|
|
788
|
-
origin: snapId
|
|
789
|
-
},
|
|
790
|
-
requestData
|
|
791
|
-
});
|
|
797
|
+
_class_private_method_get(this, _updatePermissions, updatePermissions).call(this, {
|
|
798
|
+
snapId,
|
|
799
|
+
unusedPermissions,
|
|
800
|
+
newPermissions: approvedNewPermissions,
|
|
801
|
+
requestData
|
|
802
|
+
});
|
|
803
|
+
if (manifest.initialConnections) {
|
|
804
|
+
_class_private_method_get(this, _handleInitialConnections, handleInitialConnections).call(this, snapId, oldManifest.initialConnections ?? null, manifest.initialConnections);
|
|
792
805
|
}
|
|
793
806
|
const rollbackSnapshot = _class_private_method_get(this, _getRollbackSnapshot, getRollbackSnapshot).call(this, snapId);
|
|
794
807
|
if (rollbackSnapshot !== undefined) {
|
|
795
808
|
rollbackSnapshot.permissions.revoked = unusedPermissions;
|
|
796
|
-
rollbackSnapshot.permissions.granted =
|
|
809
|
+
rollbackSnapshot.permissions.granted = approvedNewPermissions;
|
|
797
810
|
rollbackSnapshot.permissions.requestData = requestData;
|
|
798
811
|
}
|
|
799
812
|
const sourceCode = sourceCodeFile.toString();
|
|
@@ -817,11 +830,13 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
817
830
|
return truncatedSnap;
|
|
818
831
|
} catch (error) {
|
|
819
832
|
logError(`Error when updating ${snapId},`, error);
|
|
833
|
+
const errorString = error instanceof Error ? error.message : error.toString();
|
|
820
834
|
_class_private_method_get(this, _updateApproval, updateApproval).call(this, pendingApproval.id, {
|
|
821
835
|
loading: false,
|
|
822
|
-
error:
|
|
836
|
+
error: errorString,
|
|
823
837
|
type: SNAP_APPROVAL_UPDATE
|
|
824
838
|
});
|
|
839
|
+
this.messagingSystem.publish('SnapController:snapInstallFailed', snapId, origin, true, errorString);
|
|
825
840
|
throw error;
|
|
826
841
|
}
|
|
827
842
|
}
|
|
@@ -856,14 +871,13 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
856
871
|
permissions: processedPermissions
|
|
857
872
|
});
|
|
858
873
|
const { permissions: approvedPermissions, ...requestData } = await pendingApproval.promise;
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
});
|
|
874
|
+
_class_private_method_get(this, _updatePermissions, updatePermissions).call(this, {
|
|
875
|
+
snapId,
|
|
876
|
+
newPermissions: approvedPermissions,
|
|
877
|
+
requestData
|
|
878
|
+
});
|
|
879
|
+
if (snap.manifest.initialConnections) {
|
|
880
|
+
_class_private_method_get(this, _handleInitialConnections, handleInitialConnections).call(this, snapId, null, snap.manifest.initialConnections);
|
|
867
881
|
}
|
|
868
882
|
} finally{
|
|
869
883
|
const runtime = _class_private_method_get(this, _getRuntimeExpect, getRuntimeExpect).call(this, snapId);
|
|
@@ -925,7 +939,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
925
939
|
}
|
|
926
940
|
constructor({ closeAllConnections, messenger, state, dynamicPermissions = [
|
|
927
941
|
'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 }){
|
|
942
|
+
], 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
943
|
super({
|
|
930
944
|
messenger,
|
|
931
945
|
metadata: {
|
|
@@ -963,6 +977,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
963
977
|
});
|
|
964
978
|
_class_private_method_init(this, _initializeStateMachine);
|
|
965
979
|
_class_private_method_init(this, _registerMessageHandlers);
|
|
980
|
+
_class_private_method_init(this, _handlePreinstalledSnaps);
|
|
966
981
|
_class_private_method_init(this, _pollForLastRequestStatus);
|
|
967
982
|
/**
|
|
968
983
|
* Blocks an installed snap and prevents it from being started again. Emits
|
|
@@ -980,6 +995,8 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
980
995
|
*
|
|
981
996
|
* @param snapId - The snap to terminate.
|
|
982
997
|
*/ _class_private_method_init(this, _terminateSnap);
|
|
998
|
+
_class_private_method_init(this, _handleInitialConnections);
|
|
999
|
+
_class_private_method_init(this, _addSnapToSubject);
|
|
983
1000
|
_class_private_method_init(this, _removeSnapFromSubjects);
|
|
984
1001
|
_class_private_method_init(this, _revokeAllSnapPermissions);
|
|
985
1002
|
_class_private_method_init(this, _createApproval);
|
|
@@ -1007,13 +1024,6 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
1007
1024
|
* @returns An array of the names of the endowments.
|
|
1008
1025
|
*/ _class_private_method_init(this, _getEndowments);
|
|
1009
1026
|
_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
1027
|
_class_private_method_init(this, _validateSnapPermissions);
|
|
1018
1028
|
_class_private_method_init(this, _getRpcRequestHandler);
|
|
1019
1029
|
_class_private_method_init(this, _triggerPhishingListUpdate);
|
|
@@ -1058,6 +1068,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
1058
1068
|
_class_private_method_init(this, _getRuntimeExpect);
|
|
1059
1069
|
_class_private_method_init(this, _setupRuntime);
|
|
1060
1070
|
_class_private_method_init(this, _calculatePermissionsChange);
|
|
1071
|
+
_class_private_method_init(this, _updatePermissions);
|
|
1061
1072
|
_class_private_method_init(this, _isValidUpdate);
|
|
1062
1073
|
/**
|
|
1063
1074
|
* Call a lifecycle hook on a snap, if the snap has the
|
|
@@ -1154,7 +1165,10 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
1154
1165
|
});
|
|
1155
1166
|
_class_private_method_get(this, _initializeStateMachine, initializeStateMachine).call(this);
|
|
1156
1167
|
_class_private_method_get(this, _registerMessageHandlers, registerMessageHandlers).call(this);
|
|
1157
|
-
|
|
1168
|
+
if (preinstalledSnaps) {
|
|
1169
|
+
_class_private_method_get(this, _handlePreinstalledSnaps, handlePreinstalledSnaps).call(this, preinstalledSnaps);
|
|
1170
|
+
}
|
|
1171
|
+
Object.values(this.state?.snaps ?? {}).forEach((snap)=>_class_private_method_get(this, _setupRuntime, setupRuntime).call(this, snap.id));
|
|
1158
1172
|
}
|
|
1159
1173
|
}
|
|
1160
1174
|
function initializeStateMachine() {
|
|
@@ -1231,6 +1245,63 @@ function registerMessageHandlers() {
|
|
|
1231
1245
|
this.messagingSystem.registerActionHandler(`${controllerName}:revokeDynamicPermissions`, (...args)=>this.revokeDynamicSnapPermissions(...args));
|
|
1232
1246
|
this.messagingSystem.registerActionHandler(`${controllerName}:getFile`, async (...args)=>this.getSnapFile(...args));
|
|
1233
1247
|
}
|
|
1248
|
+
function handlePreinstalledSnaps(preinstalledSnaps) {
|
|
1249
|
+
for (const { snapId, manifest, files, removable } of preinstalledSnaps){
|
|
1250
|
+
const existingSnap = this.get(snapId);
|
|
1251
|
+
const isAlreadyInstalled = existingSnap !== undefined;
|
|
1252
|
+
const isUpdate = isAlreadyInstalled && gtVersion(manifest.version, existingSnap.version);
|
|
1253
|
+
// Disallow downgrades and overwriting non preinstalled snaps
|
|
1254
|
+
if (isAlreadyInstalled && (!isUpdate || existingSnap.preinstalled !== true)) {
|
|
1255
|
+
continue;
|
|
1256
|
+
}
|
|
1257
|
+
const manifestFile = new VirtualFile({
|
|
1258
|
+
path: NpmSnapFileNames.Manifest,
|
|
1259
|
+
value: JSON.stringify(manifest),
|
|
1260
|
+
result: manifest
|
|
1261
|
+
});
|
|
1262
|
+
const virtualFiles = files.map(({ path, value })=>new VirtualFile({
|
|
1263
|
+
value,
|
|
1264
|
+
path
|
|
1265
|
+
}));
|
|
1266
|
+
const { filePath, iconPath } = manifest.source.location.npm;
|
|
1267
|
+
const sourceCode = virtualFiles.find((file)=>file.path === filePath);
|
|
1268
|
+
const svgIcon = iconPath ? virtualFiles.find((file)=>file.path === iconPath) : undefined;
|
|
1269
|
+
assert(sourceCode, 'Source code not provided for preinstalled snap.');
|
|
1270
|
+
assert(!iconPath || iconPath && svgIcon, 'Icon not provided for preinstalled snap.');
|
|
1271
|
+
assert(manifest.source.files === undefined, 'Auxiliary files are not currently supported for preinstalled snaps.');
|
|
1272
|
+
const localizationFiles = manifest.source.locales?.map((path)=>virtualFiles.find((file)=>file.path === path)) ?? [];
|
|
1273
|
+
const validatedLocalizationFiles = getValidatedLocalizationFiles(localizationFiles.filter(Boolean));
|
|
1274
|
+
assert(localizationFiles.length === validatedLocalizationFiles.length, 'Missing localization files for preinstalled snap.');
|
|
1275
|
+
const filesObject = {
|
|
1276
|
+
manifest: manifestFile,
|
|
1277
|
+
sourceCode,
|
|
1278
|
+
svgIcon,
|
|
1279
|
+
auxiliaryFiles: [],
|
|
1280
|
+
localizationFiles: validatedLocalizationFiles
|
|
1281
|
+
};
|
|
1282
|
+
// Add snap to the SnapController state
|
|
1283
|
+
_class_private_method_get(this, _set, set).call(this, {
|
|
1284
|
+
id: snapId,
|
|
1285
|
+
origin: 'metamask',
|
|
1286
|
+
files: filesObject,
|
|
1287
|
+
removable,
|
|
1288
|
+
preinstalled: true
|
|
1289
|
+
});
|
|
1290
|
+
// Setup permissions
|
|
1291
|
+
const processedPermissions = processSnapPermissions(manifest.initialPermissions);
|
|
1292
|
+
_class_private_method_get(this, _validateSnapPermissions, validateSnapPermissions).call(this, processedPermissions);
|
|
1293
|
+
const { newPermissions, unusedPermissions } = _class_private_method_get(this, _calculatePermissionsChange, calculatePermissionsChange).call(this, snapId, processedPermissions);
|
|
1294
|
+
_class_private_method_get(this, _updatePermissions, updatePermissions).call(this, {
|
|
1295
|
+
snapId,
|
|
1296
|
+
newPermissions,
|
|
1297
|
+
unusedPermissions
|
|
1298
|
+
});
|
|
1299
|
+
// Set status
|
|
1300
|
+
this.update((state)=>{
|
|
1301
|
+
state.snaps[snapId].status = SnapStatus.Stopped;
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1234
1305
|
function pollForLastRequestStatus() {
|
|
1235
1306
|
_class_private_field_set(this, _timeoutForLastRequestStatus, setTimeout(()=>{
|
|
1236
1307
|
_class_private_method_get(this, _stopSnapsLastRequestPastMax, stopSnapsLastRequestPastMax).call(this).catch((error)=>{
|
|
@@ -1293,6 +1364,52 @@ async function terminateSnap(snapId) {
|
|
|
1293
1364
|
await this.messagingSystem.call('ExecutionService:terminateSnap', snapId);
|
|
1294
1365
|
this.messagingSystem.publish('SnapController:snapTerminated', this.getTruncatedExpect(snapId));
|
|
1295
1366
|
}
|
|
1367
|
+
function handleInitialConnections(snapId, previousInitialConnections, initialConnections) {
|
|
1368
|
+
if (previousInitialConnections) {
|
|
1369
|
+
const revokedInitialConnections = setDiff(previousInitialConnections, initialConnections);
|
|
1370
|
+
for (const origin of Object.keys(revokedInitialConnections)){
|
|
1371
|
+
this.removeSnapFromSubject(origin, snapId);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
for (const origin of Object.keys(initialConnections)){
|
|
1375
|
+
_class_private_method_get(this, _addSnapToSubject, addSnapToSubject).call(this, origin, snapId);
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
function addSnapToSubject(origin, snapId) {
|
|
1379
|
+
const subjectPermissions = this.messagingSystem.call('PermissionController:getPermissions', origin);
|
|
1380
|
+
const existingCaveat = subjectPermissions?.[WALLET_SNAP_PERMISSION_KEY]?.caveats?.find((caveat)=>caveat.type === SnapCaveatType.SnapIds);
|
|
1381
|
+
const subjectHasSnap = Boolean((existingCaveat?.value)?.[snapId]);
|
|
1382
|
+
// If the subject is already connected to the snap, this is a no-op.
|
|
1383
|
+
if (subjectHasSnap) {
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
// If an existing caveat exists, we add the snap to that.
|
|
1387
|
+
if (existingCaveat) {
|
|
1388
|
+
this.messagingSystem.call('PermissionController:updateCaveat', origin, WALLET_SNAP_PERMISSION_KEY, SnapCaveatType.SnapIds, {
|
|
1389
|
+
...existingCaveat,
|
|
1390
|
+
[snapId]: {}
|
|
1391
|
+
});
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
const approvedPermissions = {
|
|
1395
|
+
[WALLET_SNAP_PERMISSION_KEY]: {
|
|
1396
|
+
caveats: [
|
|
1397
|
+
{
|
|
1398
|
+
type: SnapCaveatType.SnapIds,
|
|
1399
|
+
value: {
|
|
1400
|
+
[snapId]: {}
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
]
|
|
1404
|
+
}
|
|
1405
|
+
};
|
|
1406
|
+
this.messagingSystem.call('PermissionController:grantPermissions', {
|
|
1407
|
+
approvedPermissions,
|
|
1408
|
+
subject: {
|
|
1409
|
+
origin
|
|
1410
|
+
}
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1296
1413
|
function removeSnapFromSubjects(snapId) {
|
|
1297
1414
|
const subjects = this.messagingSystem.call('PermissionController:getSubjectNames');
|
|
1298
1415
|
for (const subject of subjects){
|
|
@@ -1350,8 +1467,8 @@ async function add(args) {
|
|
|
1350
1467
|
// If fetching and setting the snap succeeds, this property will be set
|
|
1351
1468
|
// to null in the authorize() method.
|
|
1352
1469
|
runtime.installPromise = (async ()=>{
|
|
1353
|
-
const fetchedSnap = await
|
|
1354
|
-
const manifest = fetchedSnap.
|
|
1470
|
+
const fetchedSnap = await fetchSnap(snapId, location);
|
|
1471
|
+
const manifest = fetchedSnap.manifest.result;
|
|
1355
1472
|
if (!satisfiesVersionRange(manifest.version, versionRange)) {
|
|
1356
1473
|
throw new Error(`Version mismatch. Manifest for "${snapId}" specifies version "${manifest.version}" which doesn't satisfy requested version range "${versionRange}".`);
|
|
1357
1474
|
}
|
|
@@ -1361,7 +1478,7 @@ async function add(args) {
|
|
|
1361
1478
|
});
|
|
1362
1479
|
return _class_private_method_get(this, _set, set).call(this, {
|
|
1363
1480
|
...args,
|
|
1364
|
-
|
|
1481
|
+
files: fetchedSnap,
|
|
1365
1482
|
id: snapId
|
|
1366
1483
|
});
|
|
1367
1484
|
})();
|
|
@@ -1424,7 +1541,7 @@ async function getEndowments(snapId) {
|
|
|
1424
1541
|
return dedupedEndowments;
|
|
1425
1542
|
}
|
|
1426
1543
|
function set(args) {
|
|
1427
|
-
const { id: snapId, origin, files, isUpdate = false } = args;
|
|
1544
|
+
const { id: snapId, origin, files, isUpdate = false, removable, preinstalled } = args;
|
|
1428
1545
|
const { manifest, sourceCode: sourceCodeFile, svgIcon, auxiliaryFiles: rawAuxiliaryFiles, localizationFiles } = files;
|
|
1429
1546
|
assertIsSnapManifest(manifest.result);
|
|
1430
1547
|
const { version, proposedName } = manifest.result;
|
|
@@ -1455,6 +1572,8 @@ function set(args) {
|
|
|
1455
1572
|
// previous state.
|
|
1456
1573
|
blocked: false,
|
|
1457
1574
|
enabled: true,
|
|
1575
|
+
removable,
|
|
1576
|
+
preinstalled,
|
|
1458
1577
|
id: snapId,
|
|
1459
1578
|
initialPermissions: manifest.result.initialPermissions,
|
|
1460
1579
|
manifest: manifest.result,
|
|
@@ -1491,36 +1610,6 @@ function set(args) {
|
|
|
1491
1610
|
sourceCode
|
|
1492
1611
|
};
|
|
1493
1612
|
}
|
|
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
1613
|
function validateSnapPermissions(processedPermissions) {
|
|
1525
1614
|
const permissionKeys = Object.keys(processedPermissions);
|
|
1526
1615
|
const handlerPermissions = Array.from(new Set(Object.values(handlerEndowments)));
|
|
@@ -1613,6 +1702,17 @@ async function assertSnapRpcRequestResult(handlerType, result) {
|
|
|
1613
1702
|
validateComponentLinks(result.content, _class_private_method_get(this, _checkPhishingList, checkPhishingList).bind(this));
|
|
1614
1703
|
break;
|
|
1615
1704
|
}
|
|
1705
|
+
case HandlerType.OnSignature:
|
|
1706
|
+
{
|
|
1707
|
+
assertStruct(result, OnSignatureResponseStruct);
|
|
1708
|
+
// Null is an allowed return value here
|
|
1709
|
+
if (result === null) {
|
|
1710
|
+
return;
|
|
1711
|
+
}
|
|
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
|
+
break;
|
|
1715
|
+
}
|
|
1616
1716
|
case HandlerType.OnHomePage:
|
|
1617
1717
|
assertStruct(result, OnHomePageResponseStruct);
|
|
1618
1718
|
await _class_private_method_get(this, _triggerPhishingListUpdate, triggerPhishingListUpdate).call(this);
|
|
@@ -1651,11 +1751,7 @@ function createRollbackSnapshot(snapId) {
|
|
|
1651
1751
|
assert(_class_private_field_get(this, _rollbackSnapshots).get(snapId) === undefined, new Error(`Snap "${snapId}" rollback snapshot already exists.`));
|
|
1652
1752
|
_class_private_field_get(this, _rollbackSnapshots).set(snapId, {
|
|
1653
1753
|
statePatches: [],
|
|
1654
|
-
permissions: {
|
|
1655
|
-
revoked: null,
|
|
1656
|
-
granted: [],
|
|
1657
|
-
requestData: null
|
|
1658
|
-
},
|
|
1754
|
+
permissions: {},
|
|
1659
1755
|
newVersion: ''
|
|
1660
1756
|
});
|
|
1661
1757
|
const newRollbackSnapshot = _class_private_field_get(this, _rollbackSnapshots).get(snapId);
|
|
@@ -1683,20 +1779,12 @@ async function rollbackSnap(snapId) {
|
|
|
1683
1779
|
state.snaps[snapId].status = SnapStatus.Stopped;
|
|
1684
1780
|
});
|
|
1685
1781
|
}
|
|
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
|
-
}
|
|
1782
|
+
_class_private_method_get(this, _updatePermissions, updatePermissions).call(this, {
|
|
1783
|
+
snapId,
|
|
1784
|
+
unusedPermissions: permissions.granted,
|
|
1785
|
+
newPermissions: permissions.revoked,
|
|
1786
|
+
requestData: permissions.requestData
|
|
1787
|
+
});
|
|
1700
1788
|
const truncatedSnap = this.getTruncatedExpect(snapId);
|
|
1701
1789
|
this.messagingSystem.publish('SnapController:snapRolledback', truncatedSnap, rollbackSnapshot.newVersion);
|
|
1702
1790
|
_class_private_field_get(this, _rollbackSnapshots).delete(snapId);
|
|
@@ -1752,6 +1840,23 @@ function calculatePermissionsChange(snapId, desiredPermissionsSet) {
|
|
|
1752
1840
|
approvedPermissions
|
|
1753
1841
|
};
|
|
1754
1842
|
}
|
|
1843
|
+
function updatePermissions({ snapId, unusedPermissions = {}, newPermissions = {}, requestData }) {
|
|
1844
|
+
const unusedPermissionsKeys = Object.keys(unusedPermissions);
|
|
1845
|
+
if (isNonEmptyArray(unusedPermissionsKeys)) {
|
|
1846
|
+
this.messagingSystem.call('PermissionController:revokePermissions', {
|
|
1847
|
+
[snapId]: unusedPermissionsKeys
|
|
1848
|
+
});
|
|
1849
|
+
}
|
|
1850
|
+
if (isNonEmptyArray(Object.keys(newPermissions))) {
|
|
1851
|
+
this.messagingSystem.call('PermissionController:grantPermissions', {
|
|
1852
|
+
approvedPermissions: newPermissions,
|
|
1853
|
+
subject: {
|
|
1854
|
+
origin: snapId
|
|
1855
|
+
},
|
|
1856
|
+
requestData
|
|
1857
|
+
});
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1755
1860
|
function isValidUpdate(snapId, newVersionRange) {
|
|
1756
1861
|
const existingSnap = this.getExpect(snapId);
|
|
1757
1862
|
if (satisfiesVersionRange(existingSnap.version, newVersionRange)) {
|