Skip to content

Commit 2c7f8fa

Browse files
committed
Clear cache on lock
1 parent 3916cc7 commit 2c7f8fa

File tree

6 files changed

+697
-14
lines changed

6 files changed

+697
-14
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"branches": 92.41,
33
"functions": 96.56,
4-
"lines": 97.99,
5-
"statements": 97.71
4+
"lines": 98,
5+
"statements": 97.72
66
}

packages/snaps-controllers/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
"@metamask/json-rpc-engine": "^10.0.1",
8585
"@metamask/json-rpc-middleware-stream": "^8.0.5",
8686
"@metamask/key-tree": "^10.0.1",
87+
"@metamask/keyring-controller": "^19.0.2",
8788
"@metamask/object-multiplex": "^2.0.0",
8889
"@metamask/permission-controller": "^11.0.3",
8990
"@metamask/phishing-controller": "^12.0.2",

packages/snaps-controllers/src/snaps/SnapController.test.tsx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import {
5252
MOCK_SNAP_NAME,
5353
DEFAULT_SOURCE_PATH,
5454
DEFAULT_ICON_PATH,
55+
TEST_SECRET_RECOVERY_PHRASE_BYTES,
5556
} from '@metamask/snaps-utils/test-utils';
5657
import type { SemVerRange, SemVerVersion, Json } from '@metamask/utils';
5758
import {
@@ -2120,6 +2121,59 @@ describe('SnapController', () => {
21202121
await service.terminateAllSnaps();
21212122
});
21222123

2124+
it('clears encrypted state of Snaps when the client is locked', async () => {
2125+
const rootMessenger = getControllerMessenger();
2126+
const messenger = getSnapControllerMessenger(rootMessenger);
2127+
2128+
const state = { myVariable: 1 };
2129+
2130+
const mockEncryptedState = await encrypt(
2131+
ENCRYPTION_KEY,
2132+
state,
2133+
undefined,
2134+
undefined,
2135+
DEFAULT_ENCRYPTION_KEY_DERIVATION_OPTIONS,
2136+
);
2137+
2138+
const getMnemonic = jest
2139+
.fn()
2140+
.mockReturnValue(TEST_SECRET_RECOVERY_PHRASE_BYTES);
2141+
2142+
const snapController = getSnapController(
2143+
getSnapControllerOptions({
2144+
messenger,
2145+
state: {
2146+
snaps: {
2147+
[MOCK_SNAP_ID]: getPersistedSnapObject(),
2148+
},
2149+
snapStates: {
2150+
[MOCK_SNAP_ID]: mockEncryptedState,
2151+
},
2152+
},
2153+
getMnemonic,
2154+
}),
2155+
);
2156+
2157+
expect(
2158+
await messenger.call('SnapController:getSnapState', MOCK_SNAP_ID, true),
2159+
).toStrictEqual(state);
2160+
expect(getMnemonic).toHaveBeenCalledTimes(1);
2161+
2162+
rootMessenger.publish('KeyringController:lock');
2163+
2164+
expect(
2165+
await messenger.call('SnapController:getSnapState', MOCK_SNAP_ID, true),
2166+
).toStrictEqual(state);
2167+
2168+
// We assume `getMnemonic` is called again because the controller needs to
2169+
// decrypt the state again. This is not an ideal way to test this, but it
2170+
// is the easiest to test this without exposing the internal state of the
2171+
// `SnapController`.
2172+
expect(getMnemonic).toHaveBeenCalledTimes(2);
2173+
2174+
snapController.destroy();
2175+
});
2176+
21232177
describe('handleRequest', () => {
21242178
it.each(
21252179
Object.keys(handlerEndowments).filter(

packages/snaps-controllers/src/snaps/SnapController.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
} from '@metamask/base-controller';
1010
import { BaseController } from '@metamask/base-controller';
1111
import type { CryptographicFunctions } from '@metamask/key-tree';
12+
import type { KeyringControllerLockEvent } from '@metamask/keyring-controller';
1213
import type {
1314
Caveat,
1415
GetEndowments,
@@ -607,7 +608,8 @@ export type AllowedActions =
607608
export type AllowedEvents =
608609
| ExecutionServiceEvents
609610
| SnapInstalled
610-
| SnapUpdated;
611+
| SnapUpdated
612+
| KeyringControllerLockEvent;
611613

612614
type SnapControllerMessenger = RestrictedControllerMessenger<
613615
typeof controllerName,
@@ -967,6 +969,11 @@ export class SnapController extends BaseController<
967969
},
968970
);
969971

972+
this.messagingSystem.subscribe(
973+
'KeyringController:lock',
974+
this.#handleLock.bind(this),
975+
);
976+
970977
this.#initializeStateMachine();
971978
this.#registerMessageHandlers();
972979

@@ -1832,6 +1839,7 @@ export class SnapController extends BaseController<
18321839
const useCache =
18331840
this.#hasCachedEncryptionKey(snapId) ||
18341841
this.#encryptor.isVaultUpdated(state);
1842+
18351843
const { key } = await this.#getSnapEncryptionKey({
18361844
snapId,
18371845
salt,
@@ -4007,4 +4015,17 @@ export class SnapController extends BaseController<
40074015
},
40084016
});
40094017
}
4018+
4019+
/**
4020+
* Handle the `KeyringController:lock` event.
4021+
*
4022+
* Currently this clears the cached encrypted state (if any) for all Snaps.
4023+
*/
4024+
#handleLock() {
4025+
for (const runtime of this.#snapsRuntimeData.values()) {
4026+
runtime.encryptionKey = null;
4027+
runtime.encryptionSalt = null;
4028+
runtime.state = undefined;
4029+
}
4030+
}
40104031
}

packages/snaps-controllers/src/test-utils/controller.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,7 @@ export const getSnapControllerMessenger = (
469469
'SnapController:snapUpdated',
470470
'SnapController:stateChange',
471471
'SnapController:snapRolledback',
472+
'KeyringController:lock',
472473
],
473474
allowedActions: [
474475
'ApprovalController:addRequest',

0 commit comments

Comments
 (0)