diff --git a/packages/snaps-controllers/coverage.json b/packages/snaps-controllers/coverage.json index fc2973d5b5..f2b8bef1a9 100644 --- a/packages/snaps-controllers/coverage.json +++ b/packages/snaps-controllers/coverage.json @@ -1,5 +1,5 @@ { - "branches": 90.9, + "branches": 91.22, "functions": 96.58, "lines": 97.85, "statements": 97.52 diff --git a/packages/snaps-controllers/src/snaps/SnapController.test.ts b/packages/snaps-controllers/src/snaps/SnapController.test.ts index 406fa068c6..3018a02181 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.test.ts +++ b/packages/snaps-controllers/src/snaps/SnapController.test.ts @@ -538,6 +538,114 @@ describe('SnapController', () => { await service.terminateAllSnaps(); }); + it('includes the initialConnections data in the approval requestState when installing a Snap', async () => { + const rootMessenger = getControllerMessenger(); + const messenger = getSnapControllerMessenger(rootMessenger); + + rootMessenger.registerActionHandler( + 'PermissionController:getPermissions', + () => ({}), + ); + + const initialConnections = { + 'npm:filsnap': {}, + 'https://snaps.metamask.io': {}, + }; + + const { manifest } = await getMockSnapFilesWithUpdatedChecksum({ + manifest: getSnapManifest({ + initialConnections, + }), + }); + + const snapController = getSnapController( + getSnapControllerOptions({ + messenger, + detectSnapLocation: loopbackDetect({ manifest }), + }), + ); + + await snapController.installSnaps(MOCK_ORIGIN, { + [MOCK_SNAP_ID]: {}, + }); + + expect(messenger.call).toHaveBeenNthCalledWith( + 4, + 'ApprovalController:updateRequestState', + { + id: expect.any(String), + requestState: { + loading: false, + connections: initialConnections, + permissions: expect.anything(), + }, + }, + ); + + snapController.destroy(); + }); + + it('includes the initialConnections data in the approval requestState when updating a Snap', async () => { + const rootMessenger = getControllerMessenger(); + const messenger = getSnapControllerMessenger(rootMessenger); + + rootMessenger.registerActionHandler( + 'PermissionController:getPermissions', + () => ({}), + ); + + const initialConnections = { + 'npm:filsnap': {}, + 'https://snaps.metamask.io': {}, + }; + + const { manifest } = await getMockSnapFilesWithUpdatedChecksum({ + manifest: getSnapManifest({ + version: '1.1.0' as SemVerVersion, + initialConnections, + }), + }); + + const detectSnapLocation = loopbackDetect({ + manifest: manifest.result, + }); + + const snapController = getSnapController( + getSnapControllerOptions({ + messenger, + state: { + snaps: getPersistedSnapsState(), + }, + detectSnapLocation, + }), + ); + + await snapController.updateSnap( + MOCK_ORIGIN, + MOCK_SNAP_ID, + detectSnapLocation(), + ); + + expect(messenger.call).toHaveBeenNthCalledWith( + 4, + 'ApprovalController:updateRequestState', + { + id: expect.any(String), + requestState: { + connections: initialConnections, + permissions: expect.anything(), + newVersion: '1.1.0', + newPermissions: expect.anything(), + approvedPermissions: {}, + unusedPermissions: {}, + loading: false, + }, + }, + ); + + snapController.destroy(); + }); + it('installs a snap via installSnaps', async () => { const messenger = getSnapControllerMessenger(); const snapController = getSnapController( @@ -613,6 +721,7 @@ describe('SnapController', () => { id: expect.any(String), requestState: { loading: false, + connections: {}, permissions, }, }, @@ -3594,6 +3703,7 @@ describe('SnapController', () => { expect.objectContaining({ id: expect.any(String), requestState: { + connections: {}, permissions, loading: false, }, @@ -3744,6 +3854,7 @@ describe('SnapController', () => { id: expect.any(String), requestState: { loading: false, + connections: {}, permissions, }, }), @@ -3838,6 +3949,7 @@ describe('SnapController', () => { id: expect.any(String), requestState: { loading: false, + connections: {}, permissions, }, }), @@ -4463,6 +4575,7 @@ describe('SnapController', () => { id: expect.any(String), requestState: { loading: false, + connections: {}, permissions, }, }), @@ -4651,6 +4764,7 @@ describe('SnapController', () => { id: expect.any(String), requestState: { loading: false, + connections: {}, permissions: { [handlerEndowments.onRpcRequest as string]: { caveats: [ @@ -4780,6 +4894,7 @@ describe('SnapController', () => { id: expect.any(String), requestState: { loading: false, + connections: {}, permissions: { [SnapEndowments.Rpc]: { caveats: [caveat], @@ -5012,6 +5127,7 @@ describe('SnapController', () => { id: expect.any(String), requestState: { loading: false, + connections: {}, permissions: {}, newVersion, newPermissions: {}, @@ -5736,6 +5852,7 @@ describe('SnapController', () => { id: expect.any(String), requestState: { loading: false, + connections: {}, permissions: {}, newVersion: '1.1.0', newPermissions: {}, @@ -5927,6 +6044,7 @@ describe('SnapController', () => { id: expect.any(String), requestState: { loading: false, + connections: {}, permissions: {}, newVersion: '1.1.0', newPermissions: {}, @@ -6086,6 +6204,7 @@ describe('SnapController', () => { id: expect.any(String), requestState: { loading: false, + connections: {}, permissions, newVersion: '1.1.0', newPermissions: permissions, @@ -6260,6 +6379,7 @@ describe('SnapController', () => { id: expect.any(String), requestState: { loading: false, + connections: {}, permissions: { 'endowment:network-access': {} }, newVersion: '1.1.0', newPermissions: { 'endowment:network-access': {} }, diff --git a/packages/snaps-controllers/src/snaps/SnapController.ts b/packages/snaps-controllers/src/snaps/SnapController.ts index d3bfd6284f..3af96cdba3 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.ts +++ b/packages/snaps-controllers/src/snaps/SnapController.ts @@ -2386,6 +2386,7 @@ export class SnapController extends BaseController< this.#calculatePermissionsChange(snapId, processedPermissions); this.#updateApproval(pendingApproval.id, { + connections: manifest.initialConnections ?? {}, permissions: newPermissions, newVersion: manifest.version, newPermissions, @@ -2738,6 +2739,7 @@ export class SnapController extends BaseController< preinstalled, id: snapId, + initialConnections: manifest.result.initialConnections, initialPermissions: manifest.result.initialPermissions, manifest: manifest.result, status: this.#statusMachine.config.initial as StatusStates['value'], @@ -2835,7 +2837,7 @@ export class SnapController extends BaseController< log(`Authorizing snap: ${snapId}`); const snapsState = this.state.snaps; const snap = snapsState[snapId]; - const { initialPermissions } = snap; + const { initialPermissions, initialConnections } = snap; try { const processedPermissions = processSnapPermissions(initialPermissions); @@ -2844,6 +2846,7 @@ export class SnapController extends BaseController< this.#updateApproval(pendingApproval.id, { loading: false, + connections: initialConnections ?? {}, permissions: processedPermissions, }); diff --git a/packages/snaps-utils/src/snaps.ts b/packages/snaps-utils/src/snaps.ts index 5f86711e96..b0f3b05e29 100644 --- a/packages/snaps-utils/src/snaps.ts +++ b/packages/snaps-utils/src/snaps.ts @@ -26,7 +26,11 @@ import validateNPMPackage from 'validate-npm-package-name'; import { SnapCaveatType } from './caveats'; import { checksumFiles } from './checksum'; import type { LocalizationFile } from './localization'; -import type { SnapManifest, SnapPermissions } from './manifest/validation'; +import type { + InitialConnections, + SnapManifest, + SnapPermissions, +} from './manifest/validation'; import type { FetchedSnapFiles, SnapsPermissionRequest } from './types'; import { SnapIdPrefixes, SnapValidationFailureReason, uri } from './types'; import type { VirtualFile } from './virtual-file'; @@ -85,6 +89,10 @@ export type PersistedSnap = Snap; * A Snap as it exists in {@link SnapController} state. */ export type Snap = TruncatedSnap & { + /** + * The initial connections of the Snap, optional, requested on installation. + */ + initialConnections?: InitialConnections; /** * The initial permissions of the Snap, which will be requested when it is * installed.