@metamask/snaps-controllers 3.6.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 +19 -1
- package/dist/cjs/cronjob/CronjobController.js +2 -2
- package/dist/cjs/cronjob/CronjobController.js.map +1 -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 +206 -96
- 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/cronjob/CronjobController.js +2 -2
- package/dist/esm/cronjob/CronjobController.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 +208 -98
- 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/cronjob/CronjobController.d.ts +2 -2
- 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 +33 -20
- 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 +10 -10
|
@@ -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);
|
|
@@ -485,7 +499,6 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
485
499
|
delete state.snaps[snapId];
|
|
486
500
|
delete state.snapStates[snapId];
|
|
487
501
|
});
|
|
488
|
-
this.messagingSystem.publish(`SnapController:snapRemoved`, truncated);
|
|
489
502
|
// If the snap has been fully installed before, also emit snapUninstalled.
|
|
490
503
|
if (snap.status !== SnapStatus.Installing) {
|
|
491
504
|
this.messagingSystem.publish(`SnapController:snapUninstalled`, truncated);
|
|
@@ -665,6 +678,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
665
678
|
snapId,
|
|
666
679
|
type: SNAP_APPROVAL_INSTALL
|
|
667
680
|
});
|
|
681
|
+
this.messagingSystem.publish('SnapController:snapInstallStarted', snapId, origin, false);
|
|
668
682
|
// Existing snaps must be stopped before overwriting
|
|
669
683
|
if (existingSnap && this.isRunning(snapId)) {
|
|
670
684
|
await this.stopSnap(snapId, SnapStatusEvents.Stop);
|
|
@@ -698,11 +712,13 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
698
712
|
return truncated;
|
|
699
713
|
} catch (error) {
|
|
700
714
|
logError(`Error when adding ${snapId}.`, error);
|
|
715
|
+
const errorString = error instanceof Error ? error.message : error.toString();
|
|
701
716
|
_class_private_method_get(this, _updateApproval, updateApproval).call(this, pendingApproval.id, {
|
|
702
717
|
loading: false,
|
|
703
718
|
type: SNAP_APPROVAL_INSTALL,
|
|
704
|
-
error:
|
|
719
|
+
error: errorString
|
|
705
720
|
});
|
|
721
|
+
this.messagingSystem.publish('SnapController:snapInstallFailed', snapId, origin, false, errorString);
|
|
706
722
|
throw error;
|
|
707
723
|
}
|
|
708
724
|
}
|
|
@@ -734,9 +750,11 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
734
750
|
type: SNAP_APPROVAL_UPDATE
|
|
735
751
|
});
|
|
736
752
|
try {
|
|
753
|
+
this.messagingSystem.publish('SnapController:snapInstallStarted', snapId, origin, true);
|
|
737
754
|
const snap = this.getExpect(snapId);
|
|
738
|
-
const
|
|
739
|
-
const
|
|
755
|
+
const oldManifest = snap.manifest;
|
|
756
|
+
const newSnap = await fetchSnap(snapId, location);
|
|
757
|
+
const { sourceCode: sourceCodeFile, manifest: manifestFile } = newSnap;
|
|
740
758
|
const manifest = manifestFile.result;
|
|
741
759
|
const newVersion = manifest.version;
|
|
742
760
|
if (!gtVersion(newVersion, snap.version)) {
|
|
@@ -773,28 +791,22 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
773
791
|
_class_private_method_get(this, _set, set).call(this, {
|
|
774
792
|
origin,
|
|
775
793
|
id: snapId,
|
|
776
|
-
files: newSnap
|
|
794
|
+
files: newSnap,
|
|
777
795
|
isUpdate: true
|
|
778
796
|
});
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
}
|
|
785
|
-
if (
|
|
786
|
-
this.
|
|
787
|
-
approvedPermissions: approvedNewPermissions,
|
|
788
|
-
subject: {
|
|
789
|
-
origin: snapId
|
|
790
|
-
},
|
|
791
|
-
requestData
|
|
792
|
-
});
|
|
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);
|
|
793
805
|
}
|
|
794
806
|
const rollbackSnapshot = _class_private_method_get(this, _getRollbackSnapshot, getRollbackSnapshot).call(this, snapId);
|
|
795
807
|
if (rollbackSnapshot !== undefined) {
|
|
796
808
|
rollbackSnapshot.permissions.revoked = unusedPermissions;
|
|
797
|
-
rollbackSnapshot.permissions.granted =
|
|
809
|
+
rollbackSnapshot.permissions.granted = approvedNewPermissions;
|
|
798
810
|
rollbackSnapshot.permissions.requestData = requestData;
|
|
799
811
|
}
|
|
800
812
|
const sourceCode = sourceCodeFile.toString();
|
|
@@ -818,11 +830,13 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
818
830
|
return truncatedSnap;
|
|
819
831
|
} catch (error) {
|
|
820
832
|
logError(`Error when updating ${snapId},`, error);
|
|
833
|
+
const errorString = error instanceof Error ? error.message : error.toString();
|
|
821
834
|
_class_private_method_get(this, _updateApproval, updateApproval).call(this, pendingApproval.id, {
|
|
822
835
|
loading: false,
|
|
823
|
-
error:
|
|
836
|
+
error: errorString,
|
|
824
837
|
type: SNAP_APPROVAL_UPDATE
|
|
825
838
|
});
|
|
839
|
+
this.messagingSystem.publish('SnapController:snapInstallFailed', snapId, origin, true, errorString);
|
|
826
840
|
throw error;
|
|
827
841
|
}
|
|
828
842
|
}
|
|
@@ -857,14 +871,13 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
857
871
|
permissions: processedPermissions
|
|
858
872
|
});
|
|
859
873
|
const { permissions: approvedPermissions, ...requestData } = await pendingApproval.promise;
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
});
|
|
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);
|
|
868
881
|
}
|
|
869
882
|
} finally{
|
|
870
883
|
const runtime = _class_private_method_get(this, _getRuntimeExpect, getRuntimeExpect).call(this, snapId);
|
|
@@ -926,7 +939,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
926
939
|
}
|
|
927
940
|
constructor({ closeAllConnections, messenger, state, dynamicPermissions = [
|
|
928
941
|
'eth_accounts'
|
|
929
|
-
], 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 }){
|
|
930
943
|
super({
|
|
931
944
|
messenger,
|
|
932
945
|
metadata: {
|
|
@@ -964,6 +977,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
964
977
|
});
|
|
965
978
|
_class_private_method_init(this, _initializeStateMachine);
|
|
966
979
|
_class_private_method_init(this, _registerMessageHandlers);
|
|
980
|
+
_class_private_method_init(this, _handlePreinstalledSnaps);
|
|
967
981
|
_class_private_method_init(this, _pollForLastRequestStatus);
|
|
968
982
|
/**
|
|
969
983
|
* Blocks an installed snap and prevents it from being started again. Emits
|
|
@@ -981,6 +995,8 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
981
995
|
*
|
|
982
996
|
* @param snapId - The snap to terminate.
|
|
983
997
|
*/ _class_private_method_init(this, _terminateSnap);
|
|
998
|
+
_class_private_method_init(this, _handleInitialConnections);
|
|
999
|
+
_class_private_method_init(this, _addSnapToSubject);
|
|
984
1000
|
_class_private_method_init(this, _removeSnapFromSubjects);
|
|
985
1001
|
_class_private_method_init(this, _revokeAllSnapPermissions);
|
|
986
1002
|
_class_private_method_init(this, _createApproval);
|
|
@@ -1008,13 +1024,6 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
1008
1024
|
* @returns An array of the names of the endowments.
|
|
1009
1025
|
*/ _class_private_method_init(this, _getEndowments);
|
|
1010
1026
|
_class_private_method_init(this, _set);
|
|
1011
|
-
/**
|
|
1012
|
-
* Fetches the manifest and source code of a snap.
|
|
1013
|
-
*
|
|
1014
|
-
* @param snapId - The id of the Snap.
|
|
1015
|
-
* @param location - Source from which snap will be fetched.
|
|
1016
|
-
* @returns A tuple of the Snap manifest object and the Snap source code.
|
|
1017
|
-
*/ _class_private_method_init(this, _fetchSnap);
|
|
1018
1027
|
_class_private_method_init(this, _validateSnapPermissions);
|
|
1019
1028
|
_class_private_method_init(this, _getRpcRequestHandler);
|
|
1020
1029
|
_class_private_method_init(this, _triggerPhishingListUpdate);
|
|
@@ -1059,6 +1068,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
1059
1068
|
_class_private_method_init(this, _getRuntimeExpect);
|
|
1060
1069
|
_class_private_method_init(this, _setupRuntime);
|
|
1061
1070
|
_class_private_method_init(this, _calculatePermissionsChange);
|
|
1071
|
+
_class_private_method_init(this, _updatePermissions);
|
|
1062
1072
|
_class_private_method_init(this, _isValidUpdate);
|
|
1063
1073
|
/**
|
|
1064
1074
|
* Call a lifecycle hook on a snap, if the snap has the
|
|
@@ -1155,7 +1165,10 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
1155
1165
|
});
|
|
1156
1166
|
_class_private_method_get(this, _initializeStateMachine, initializeStateMachine).call(this);
|
|
1157
1167
|
_class_private_method_get(this, _registerMessageHandlers, registerMessageHandlers).call(this);
|
|
1158
|
-
|
|
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));
|
|
1159
1172
|
}
|
|
1160
1173
|
}
|
|
1161
1174
|
function initializeStateMachine() {
|
|
@@ -1232,6 +1245,63 @@ function registerMessageHandlers() {
|
|
|
1232
1245
|
this.messagingSystem.registerActionHandler(`${controllerName}:revokeDynamicPermissions`, (...args)=>this.revokeDynamicSnapPermissions(...args));
|
|
1233
1246
|
this.messagingSystem.registerActionHandler(`${controllerName}:getFile`, async (...args)=>this.getSnapFile(...args));
|
|
1234
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
|
+
}
|
|
1235
1305
|
function pollForLastRequestStatus() {
|
|
1236
1306
|
_class_private_field_set(this, _timeoutForLastRequestStatus, setTimeout(()=>{
|
|
1237
1307
|
_class_private_method_get(this, _stopSnapsLastRequestPastMax, stopSnapsLastRequestPastMax).call(this).catch((error)=>{
|
|
@@ -1294,6 +1364,52 @@ async function terminateSnap(snapId) {
|
|
|
1294
1364
|
await this.messagingSystem.call('ExecutionService:terminateSnap', snapId);
|
|
1295
1365
|
this.messagingSystem.publish('SnapController:snapTerminated', this.getTruncatedExpect(snapId));
|
|
1296
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
|
+
}
|
|
1297
1413
|
function removeSnapFromSubjects(snapId) {
|
|
1298
1414
|
const subjects = this.messagingSystem.call('PermissionController:getSubjectNames');
|
|
1299
1415
|
for (const subject of subjects){
|
|
@@ -1351,8 +1467,8 @@ async function add(args) {
|
|
|
1351
1467
|
// If fetching and setting the snap succeeds, this property will be set
|
|
1352
1468
|
// to null in the authorize() method.
|
|
1353
1469
|
runtime.installPromise = (async ()=>{
|
|
1354
|
-
const fetchedSnap = await
|
|
1355
|
-
const manifest = fetchedSnap.
|
|
1470
|
+
const fetchedSnap = await fetchSnap(snapId, location);
|
|
1471
|
+
const manifest = fetchedSnap.manifest.result;
|
|
1356
1472
|
if (!satisfiesVersionRange(manifest.version, versionRange)) {
|
|
1357
1473
|
throw new Error(`Version mismatch. Manifest for "${snapId}" specifies version "${manifest.version}" which doesn't satisfy requested version range "${versionRange}".`);
|
|
1358
1474
|
}
|
|
@@ -1362,7 +1478,7 @@ async function add(args) {
|
|
|
1362
1478
|
});
|
|
1363
1479
|
return _class_private_method_get(this, _set, set).call(this, {
|
|
1364
1480
|
...args,
|
|
1365
|
-
|
|
1481
|
+
files: fetchedSnap,
|
|
1366
1482
|
id: snapId
|
|
1367
1483
|
});
|
|
1368
1484
|
})();
|
|
@@ -1425,10 +1541,10 @@ async function getEndowments(snapId) {
|
|
|
1425
1541
|
return dedupedEndowments;
|
|
1426
1542
|
}
|
|
1427
1543
|
function set(args) {
|
|
1428
|
-
const { id: snapId, origin, files, isUpdate = false } = args;
|
|
1544
|
+
const { id: snapId, origin, files, isUpdate = false, removable, preinstalled } = args;
|
|
1429
1545
|
const { manifest, sourceCode: sourceCodeFile, svgIcon, auxiliaryFiles: rawAuxiliaryFiles, localizationFiles } = files;
|
|
1430
1546
|
assertIsSnapManifest(manifest.result);
|
|
1431
|
-
const { version } = manifest.result;
|
|
1547
|
+
const { version, proposedName } = manifest.result;
|
|
1432
1548
|
const sourceCode = sourceCodeFile.toString();
|
|
1433
1549
|
assert(typeof sourceCode === 'string' && sourceCode.length > 0, `Invalid source code for snap "${snapId}".`);
|
|
1434
1550
|
const auxiliaryFiles = rawAuxiliaryFiles.map((file)=>{
|
|
@@ -1456,6 +1572,8 @@ function set(args) {
|
|
|
1456
1572
|
// previous state.
|
|
1457
1573
|
blocked: false,
|
|
1458
1574
|
enabled: true,
|
|
1575
|
+
removable,
|
|
1576
|
+
preinstalled,
|
|
1459
1577
|
id: snapId,
|
|
1460
1578
|
initialPermissions: manifest.result.initialPermissions,
|
|
1461
1579
|
manifest: manifest.result,
|
|
@@ -1480,42 +1598,18 @@ function set(args) {
|
|
|
1480
1598
|
rollbackSnapshot.statePatches = inversePatches;
|
|
1481
1599
|
}
|
|
1482
1600
|
}
|
|
1483
|
-
this.messagingSystem.
|
|
1601
|
+
this.messagingSystem.call('SubjectMetadataController:addSubjectMetadata', {
|
|
1602
|
+
subjectType: SubjectType.Snap,
|
|
1603
|
+
name: proposedName,
|
|
1604
|
+
origin: snap.id,
|
|
1605
|
+
version,
|
|
1606
|
+
svgIcon: svgIcon?.toString() ?? null
|
|
1607
|
+
});
|
|
1484
1608
|
return {
|
|
1485
1609
|
...snap,
|
|
1486
1610
|
sourceCode
|
|
1487
1611
|
};
|
|
1488
1612
|
}
|
|
1489
|
-
async function fetchSnap(snapId, location) {
|
|
1490
|
-
try {
|
|
1491
|
-
const manifest = await location.manifest();
|
|
1492
|
-
const sourceCode = await location.fetch(manifest.result.source.location.npm.filePath);
|
|
1493
|
-
const { iconPath } = manifest.result.source.location.npm;
|
|
1494
|
-
const svgIcon = iconPath ? await location.fetch(iconPath) : undefined;
|
|
1495
|
-
const auxiliaryFiles = await getSnapFiles(location, manifest.result.source.files);
|
|
1496
|
-
await Promise.all(auxiliaryFiles.map(async (file)=>{
|
|
1497
|
-
// This should still be safe
|
|
1498
|
-
// eslint-disable-next-line require-atomic-updates
|
|
1499
|
-
file.data.base64 = await encodeBase64(file);
|
|
1500
|
-
}));
|
|
1501
|
-
const localizationFiles = await getSnapFiles(location, manifest.result.source.locales);
|
|
1502
|
-
const validatedLocalizationFiles = getValidatedLocalizationFiles(localizationFiles);
|
|
1503
|
-
const files = {
|
|
1504
|
-
manifest,
|
|
1505
|
-
sourceCode,
|
|
1506
|
-
svgIcon,
|
|
1507
|
-
auxiliaryFiles,
|
|
1508
|
-
localizationFiles: validatedLocalizationFiles
|
|
1509
|
-
};
|
|
1510
|
-
await validateFetchedSnap(files);
|
|
1511
|
-
return {
|
|
1512
|
-
files,
|
|
1513
|
-
location
|
|
1514
|
-
};
|
|
1515
|
-
} catch (error) {
|
|
1516
|
-
throw new Error(`Failed to fetch snap "${snapId}": ${getErrorMessage(error)}.`);
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
1613
|
function validateSnapPermissions(processedPermissions) {
|
|
1520
1614
|
const permissionKeys = Object.keys(processedPermissions);
|
|
1521
1615
|
const handlerPermissions = Array.from(new Set(Object.values(handlerEndowments)));
|
|
@@ -1608,6 +1702,17 @@ async function assertSnapRpcRequestResult(handlerType, result) {
|
|
|
1608
1702
|
validateComponentLinks(result.content, _class_private_method_get(this, _checkPhishingList, checkPhishingList).bind(this));
|
|
1609
1703
|
break;
|
|
1610
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
|
+
}
|
|
1611
1716
|
case HandlerType.OnHomePage:
|
|
1612
1717
|
assertStruct(result, OnHomePageResponseStruct);
|
|
1613
1718
|
await _class_private_method_get(this, _triggerPhishingListUpdate, triggerPhishingListUpdate).call(this);
|
|
@@ -1646,11 +1751,7 @@ function createRollbackSnapshot(snapId) {
|
|
|
1646
1751
|
assert(_class_private_field_get(this, _rollbackSnapshots).get(snapId) === undefined, new Error(`Snap "${snapId}" rollback snapshot already exists.`));
|
|
1647
1752
|
_class_private_field_get(this, _rollbackSnapshots).set(snapId, {
|
|
1648
1753
|
statePatches: [],
|
|
1649
|
-
permissions: {
|
|
1650
|
-
revoked: null,
|
|
1651
|
-
granted: [],
|
|
1652
|
-
requestData: null
|
|
1653
|
-
},
|
|
1754
|
+
permissions: {},
|
|
1654
1755
|
newVersion: ''
|
|
1655
1756
|
});
|
|
1656
1757
|
const newRollbackSnapshot = _class_private_field_get(this, _rollbackSnapshots).get(snapId);
|
|
@@ -1678,20 +1779,12 @@ async function rollbackSnap(snapId) {
|
|
|
1678
1779
|
state.snaps[snapId].status = SnapStatus.Stopped;
|
|
1679
1780
|
});
|
|
1680
1781
|
}
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
requestData: permissions.requestData
|
|
1688
|
-
});
|
|
1689
|
-
}
|
|
1690
|
-
if (permissions.granted?.length) {
|
|
1691
|
-
this.messagingSystem.call('PermissionController:revokePermissions', {
|
|
1692
|
-
[snapId]: permissions.granted
|
|
1693
|
-
});
|
|
1694
|
-
}
|
|
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
|
+
});
|
|
1695
1788
|
const truncatedSnap = this.getTruncatedExpect(snapId);
|
|
1696
1789
|
this.messagingSystem.publish('SnapController:snapRolledback', truncatedSnap, rollbackSnapshot.newVersion);
|
|
1697
1790
|
_class_private_field_get(this, _rollbackSnapshots).delete(snapId);
|
|
@@ -1747,6 +1840,23 @@ function calculatePermissionsChange(snapId, desiredPermissionsSet) {
|
|
|
1747
1840
|
approvedPermissions
|
|
1748
1841
|
};
|
|
1749
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
|
+
}
|
|
1750
1860
|
function isValidUpdate(snapId, newVersionRange) {
|
|
1751
1861
|
const existingSnap = this.getExpect(snapId);
|
|
1752
1862
|
if (satisfiesVersionRange(existingSnap.version, newVersionRange)) {
|