Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions app/scripts/lib/state-utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import {
SecretType,
SeedlessOnboardingControllerState,
} from '@metamask/seedless-onboarding-controller';
import { AuthenticationControllerState } from '@metamask/profile-sync-controller/auth';
import { KeyringControllerState } from '@metamask/keyring-controller';
import { sanitizeUIState } from './state-utils';

describe('State Utils', () => {
Expand Down Expand Up @@ -57,4 +63,98 @@ describe('State Utils', () => {
});
});
});

it('sanitizes keyring controller state', () => {
const state: Partial<KeyringControllerState> = {
vault: 'vault',
encryptionKey: 'encryptionKey',
encryptionSalt: 'encryptionSalt',
};

const sanitizedState = sanitizeUIState(state);

expect(sanitizedState).toStrictEqual({});
});

it('sanitizes authentication controller state', () => {
const state: Partial<AuthenticationControllerState> = {
srpSessionData: {
test1: {
token: {
accessToken: 'accessToken', // to be sanitized
expiresIn: 0,
obtainedAt: 0,
},
profile: {
identifierId: '',
profileId: '',
metaMetricsId: '',
},
},
},
};

const sanitizedState = sanitizeUIState(state);

expect(sanitizedState).toStrictEqual({
srpSessionData: {
test1: {
token: {
expiresIn: 0,
obtainedAt: 0,
},
profile: {
identifierId: '',
profileId: '',
metaMetricsId: '',
},
},
},
});
});

it('sanitizes seedless onboarding controller state', () => {
const state: Partial<SeedlessOnboardingControllerState> = {
vault: 'vault',
vaultEncryptionKey: 'vaultEncryptionKey',
vaultEncryptionSalt: 'vaultEncryptionSalt',
encryptedSeedlessEncryptionKey: 'encryptedSeedlessEncryptionKey',
encryptedKeyringEncryptionKey: 'encryptedKeyringEncryptionKey',
accessToken: 'accessToken',
metadataAccessToken: 'metadataAccessToken',
refreshToken: 'refreshToken',
revokeToken: 'revokeToken',
nodeAuthTokens: [
{
authToken: 'authToken', // to be sanitized
nodeIndex: 1,
nodePubKey: 'nodeUrl',
},
],
socialBackupsMetadata: [
{
hash: 'hash', // to be sanitized
type: SecretType.Mnemonic,
keyringId: 'keyringId',
},
],
};

const sanitizedState = sanitizeUIState(state);

expect(sanitizedState).toStrictEqual({
nodeAuthTokens: [
{
nodeIndex: 1,
nodePubKey: 'nodeUrl',
},
],
socialBackupsMetadata: [
{
type: SecretType.Mnemonic,
keyringId: 'keyringId',
},
],
});
});
});
83 changes: 82 additions & 1 deletion app/scripts/lib/state-utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { AuthenticationControllerState } from '@metamask/profile-sync-controller/auth';
import { SeedlessOnboardingControllerState } from '@metamask/seedless-onboarding-controller';
import { SnapControllerState } from '@metamask/snaps-controllers';
import { Snap } from '@metamask/snaps-utils';

Expand All @@ -8,12 +10,16 @@ type FlattenedUIState = Record<string, any>;
const REMOVE_KEYS = [
'snapStates',
'unencryptedSnapStates',
'vault',
'phishingLists',
'whitelist',
'hotlistLastFetched',
'stalelistLastFetched',
'c2DomainBlocklistLastFetched',

// Keyring controller
'vault',
'encryptionKey',
'encryptionSalt',
];

export function sanitizeUIState(state: FlattenedUIState): FlattenedUIState {
Expand All @@ -24,6 +30,8 @@ export function sanitizeUIState(state: FlattenedUIState): FlattenedUIState {
}

sanitizeSnapData(newState);
sanitizeAuthenticationControllerState(newState);
sanitizeSeedlessOnboardingControllerState(newState);

return newState;
}
Expand Down Expand Up @@ -54,3 +62,76 @@ function stripLargeSnapData(snapData: Snap): Partial<Snap> {

return newData;
}

function sanitizeAuthenticationControllerState(state: FlattenedUIState) {
const srpSessionData = state.srpSessionData as
| AuthenticationControllerState['srpSessionData']
| undefined;

if (!srpSessionData) {
return;
}

state.srpSessionData = Object.entries(srpSessionData).reduce(
(acc, [key, value]) => {
const token = {
...value.token,
};
// @ts-expect-error - Intentionally sanitizing a required field.
delete token.accessToken;
acc[key] = {
...value,
token,
};
return acc;
},
{} as NonNullable<AuthenticationControllerState['srpSessionData']>,
);
}

function sanitizeSeedlessOnboardingControllerState(state: FlattenedUIState) {
const toDelete = [
'vault',
'vaultEncryptionKey',
'vaultEncryptionSalt',
'encryptedSeedlessEncryptionKey',
'encryptedKeyringEncryptionKey',
'accessToken',
'metadataAccessToken',
'refreshToken',
'revokeToken',
];
for (const key of toDelete) {
delete state[key];
}

// Manually sanitize the nodeAuthTokens.
const nodeAuthTokens =
state.nodeAuthTokens as SeedlessOnboardingControllerState['nodeAuthTokens'];

if (nodeAuthTokens) {
state.nodeAuthTokens = nodeAuthTokens.map((token) => {
const sanitizedToken = {
...token,
};
// @ts-expect-error - Intentionally sanitizing a required field.
delete sanitizedToken.authToken;
return sanitizedToken;
});
}

// Manually sanitize the socialBackupsMetadata.
const socialBackupsMetadata =
state.socialBackupsMetadata as SeedlessOnboardingControllerState['socialBackupsMetadata'];

if (socialBackupsMetadata) {
state.socialBackupsMetadata = socialBackupsMetadata.map((backup) => {
const sanitizedBackup = {
...backup,
};
// @ts-expect-error - Intentionally sanitizing a required field.
delete sanitizedBackup.hash;
return sanitizedBackup;
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,6 @@
"submitHistory": "object",
"delegations": "object",
"accountTree": "object",
"encryptionKey": "string",
"encryptionSalt": "string",
"socialBackupsMetadata": "object",
"srpSessionData": "object",
"enableEnforcedSimulations": true,
Expand Down
2 changes: 0 additions & 2 deletions test/integration/data/onboarding-completion-route.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,6 @@
"0xaa36a7": true
}
},
"encryptionKey": "{\"key\":{\"alg\":\"A256GCM\",\"ext\":true,\"k\":\"IWP6tqC2WeYfTxhHhTFU2m1hhO9GD5YhgYe2iRzu5GU\",\"key_ops\":[\"encrypt\",\"decrypt\"],\"kty\":\"oct\"},\"derivationOptions\":{\"algorithm\":\"PBKDF2\",\"params\":{\"iterations\":600000}}}",
"encryptionSalt": "6AA1VP9N1aU5bglaSiAhmE9PdLH3YCqiKJagv+wQCR8=",
"enforcedSimulationsSlippage": "number",
"enforcedSimulationsSlippageForTransactions": "object",
"ensEntries": {},
Expand Down
Loading