Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Version v10.34.5 #20482

Merged
merged 23 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
72d20c9
Version v10.34.5
danjm Aug 16, 2023
442181d
Log before and after each migration run (#20424)
danjm Aug 15, 2023
594dde5
Add additional Sentry E2E tests (#20425)
Gudahtt Aug 16, 2023
789122d
Capture app and migration version (#20458)
danjm Aug 16, 2023
1ab0c9e
Fix Sentry error e2e tests (#20479)
Gudahtt Aug 16, 2023
e7b113c
Bump SES to fix audit failure (#20434)
FrederikBolding Aug 14, 2023
83c8a6b
Update `protobufjs` (#20469)
Gudahtt Aug 16, 2023
d610aad
Split Sentry mask into UI and background masks (#20426)
Gudahtt Aug 16, 2023
1ad47c6
Use unflattened state for Sentry (#20428)
Gudahtt Aug 16, 2023
915bf2a
Capture exception with sentry when invariant conditions are met in mi…
danjm Aug 16, 2023
dc7ebe9
Add additional validation for persisted state metadata (#20462)
Gudahtt Aug 16, 2023
805ce29
Add types of hidden properties to Sentry data (#20457)
Gudahtt Aug 16, 2023
80746e6
Improve Sentry state pre-initialization (#20491)
Gudahtt Aug 17, 2023
2cd60d9
Remove invalid `tokensChainsCache` state (#20495)
Gudahtt Aug 17, 2023
01b009c
Run yarn dedupe to deal with unused ses dependency in yarn.lock, for …
danjm Aug 17, 2023
1d13008
Fix Sentry breadcrumbs collection during initialization (#20521)
Gudahtt Aug 18, 2023
3e26da4
Initialize composable observable store after update (#20468)
Gudahtt Aug 17, 2023
c216343
Fix and test log.info calls run for each migration (#20517)
danjm Aug 18, 2023
fa778d5
Update snapshot tests for errors.spec.js
danjm Aug 18, 2023
016a1ef
Remove GHSA-h755-8qp9-cq8 from advisory exclusions because yarn audit…
danjm Aug 18, 2023
0a3241e
Fix pre-initialization UI error state capture (#20529)
Gudahtt Aug 18, 2023
ddeaeb5
Fix Sentry breadcrumb collection during initialization (again) (#20532)
Gudahtt Aug 18, 2023
f83c7ff
Update changelog for v10.34.5 (#20533)
Gudahtt Aug 18, 2023
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
5 changes: 0 additions & 5 deletions .iyarc
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,3 @@ GHSA-257v-vj4p-3w2h
# request library is subject to SSRF.
# addressed by temporary patch in .yarn/patches/request-npm-2.88.2-f4a57c72c4.patch
GHSA-p8p7-x288-28g6

# Prototype pollution
# Not easily patched
# Minimal risk to us because we're using lockdown which also prevents this case of prototype pollution
GHSA-h755-8qp9-cq85
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [10.34.5]
### Changed
- Improve error diagnostic information
- Add additional logging for state migrations ([#20424](https://github.com/MetaMask/metamask-extension/pull/20424), [#20517](https://github.com/MetaMask/metamask-extension/pull/20517), [#20521](https://github.com/MetaMask/metamask-extension/pull/20521))
- Improve diagnostic state snapshot ([#20457](https://github.com/MetaMask/metamask-extension/pull/20457), [#20491](https://github.com/MetaMask/metamask-extension/pull/20491), [#20428](https://github.com/MetaMask/metamask-extension/pull/20428), [#20458](https://github.com/MetaMask/metamask-extension/pull/20458))
- Capture additional errors when state migrations fail ([#20427](https://github.com/MetaMask/metamask-extension/pull/20427))

### Fixed
- Fix bug where state was temporarily incomplete upon initial load ([#20468](https://github.com/MetaMask/metamask-extension/pull/20468))
- In rare circumstances, this bug may have resulted in data loss (of preferences, permissions, or tracked NFTs/tokens) or UI crashes.

## [10.34.4]
### Changed
- Updated snaps execution environment ([#20420](https://github.com/MetaMask/metamask-extension/pull/20420))
Expand Down Expand Up @@ -3885,7 +3896,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Uncategorized
- Added the ability to restore accounts from seed words.

[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.34.4...HEAD
[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.34.5...HEAD
[10.34.5]: https://github.com/MetaMask/metamask-extension/compare/v10.34.4...v10.34.5
[10.34.4]: https://github.com/MetaMask/metamask-extension/compare/v10.34.3...v10.34.4
[10.34.3]: https://github.com/MetaMask/metamask-extension/compare/v10.34.2...v10.34.3
[10.34.2]: https://github.com/MetaMask/metamask-extension/compare/v10.34.1...v10.34.2
Expand Down
54 changes: 36 additions & 18 deletions app/scripts/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
* @file The entry point for the web extension singleton process.
*/

// This import sets up a global function required for Sentry to function.
// Disabled to allow setting up initial state hooks first

// This import sets up global functions required for Sentry to function.
// It must be run first in case an error is thrown later during initialization.
import './lib/setup-persisted-state-hook';
import './lib/setup-initial-state-hooks';

import EventEmitter from 'events';
import endOfStream from 'end-of-stream';
Expand All @@ -13,6 +15,7 @@ import debounce from 'debounce-stream';
import log from 'loglevel';
import browser from 'webextension-polyfill';
import { storeAsStream } from '@metamask/obs-store';
import { isObject } from '@metamask/utils';
///: BEGIN:ONLY_INCLUDE_IN(snaps)
import { ApprovalType } from '@metamask/controller-utils';
///: END:ONLY_INCLUDE_IN
Expand Down Expand Up @@ -41,7 +44,7 @@ import Migrator from './lib/migrator';
import ExtensionPlatform from './platforms/extension';
import LocalStore from './lib/local-store';
import ReadOnlyNetworkStore from './lib/network-store';
import { SENTRY_STATE } from './lib/setupSentry';
import { SENTRY_BACKGROUND_STATE } from './lib/setupSentry';

import createStreamSink from './lib/createStreamSink';
import NotificationManager, {
Expand All @@ -68,6 +71,12 @@ import DesktopManager from '@metamask/desktop/dist/desktop-manager';
///: END:ONLY_INCLUDE_IN
/* eslint-enable import/order */

// Setup global hook for improved Sentry state snapshots during initialization
const inTest = process.env.IN_TEST;
const localStore = inTest ? new ReadOnlyNetworkStore() : new LocalStore();
global.stateHooks.getMostRecentPersistedState = () =>
localStore.mostRecentRetrievedState;

const { sentry } = global;
const firstTimeState = { ...rawFirstTimeState };

Expand All @@ -79,7 +88,7 @@ const metamaskInternalProcessHash = {

const metamaskBlockedPorts = ['trezor-connect'];

log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'info');
log.setLevel(process.env.METAMASK_DEBUG ? 'debug' : 'info', false);

const platform = new ExtensionPlatform();
const notificationManager = new NotificationManager();
Expand All @@ -90,10 +99,6 @@ let uiIsTriggering = false;
const openMetamaskTabsIDs = {};
const requestAccountTabIds = {};
let controller;

// state persistence
const inTest = process.env.IN_TEST;
const localStore = inTest ? new ReadOnlyNetworkStore() : new LocalStore();
let versionedData;

if (inTest || process.env.METAMASK_DEBUG) {
Expand Down Expand Up @@ -264,7 +269,8 @@ browser.runtime.onConnectExternal.addListener(async (...args) => {
*/
async function initialize() {
try {
const initState = await loadStateFromPersistence();
const initData = await loadStateFromPersistence();
const initState = initData.data;
const initLangCode = await getFirstPreferredLangCode();

///: BEGIN:ONLY_INCLUDE_IN(desktop)
Expand All @@ -287,6 +293,7 @@ async function initialize() {
initLangCode,
{},
isFirstMetaMaskControllerSetup,
initData.meta,
);
if (!isManifestV3) {
await loadPhishingWarningPage();
Expand Down Expand Up @@ -409,6 +416,19 @@ export async function loadStateFromPersistence() {
versionedData = await migrator.migrateData(versionedData);
if (!versionedData) {
throw new Error('MetaMask - migrator returned undefined');
} else if (!isObject(versionedData.meta)) {
throw new Error(
`MetaMask - migrator metadata has invalid type '${typeof versionedData.meta}'`,
);
} else if (typeof versionedData.meta.version !== 'number') {
throw new Error(
`MetaMask - migrator metadata version has invalid type '${typeof versionedData
.meta.version}'`,
);
} else if (!isObject(versionedData.data)) {
throw new Error(
`MetaMask - migrator data has invalid type '${typeof versionedData.data}'`,
);
}
// this initializes the meta/version data as a class variable to be used for future writes
localStore.setMetadata(versionedData.meta);
Expand All @@ -417,7 +437,7 @@ export async function loadStateFromPersistence() {
localStore.set(versionedData.data);

// return just the data
return versionedData.data;
return versionedData;
}

/**
Expand All @@ -430,12 +450,14 @@ export async function loadStateFromPersistence() {
* @param {string} initLangCode - The region code for the language preferred by the current user.
* @param {object} overrides - object with callbacks that are allowed to override the setup controller logic (usefull for desktop app)
* @param isFirstMetaMaskControllerSetup
* @param {object} stateMetadata - Metadata about the initial state and migrations, including the most recent migration version
*/
export function setupController(
initState,
initLangCode,
overrides,
isFirstMetaMaskControllerSetup,
stateMetadata,
) {
//
// MetaMask Controller
Expand All @@ -462,6 +484,7 @@ export function setupController(
localStore,
overrides,
isFirstMetaMaskControllerSetup,
currentMigrationVersion: stateMetadata.version,
});

setupEnsIpfsResolver({
Expand Down Expand Up @@ -870,14 +893,9 @@ browser.runtime.onInstalled.addListener(({ reason }) => {
});

function setupSentryGetStateGlobal(store) {
global.stateHooks.getSentryState = function () {
const fullState = store.getState();
const debugState = maskObject({ metamask: fullState }, SENTRY_STATE);
return {
browser: window.navigator.userAgent,
store: debugState,
version: platform.getVersion(),
};
global.stateHooks.getSentryAppState = function () {
const backgroundState = store.memStore.getState();
return maskObject(backgroundState, SENTRY_BACKGROUND_STATE);
};
}

Expand Down
104 changes: 104 additions & 0 deletions app/scripts/controllers/app-metadata.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import assert from 'assert';
import AppMetadataController from './app-metadata';

const EXPECTED_DEFAULT_STATE = {
currentAppVersion: '',
previousAppVersion: '',
previousMigrationVersion: 0,
currentMigrationVersion: 0,
};

describe('AppMetadataController', () => {
describe('constructor', () => {
it('accepts initial state and does not modify it if currentMigrationVersion and platform.getVersion() match respective values in state', async () => {
const initState = {
currentAppVersion: '1',
previousAppVersion: '1',
previousMigrationVersion: 1,
currentMigrationVersion: 1,
};
const appMetadataController = new AppMetadataController({
state: initState,
currentMigrationVersion: 1,
currentAppVersion: '1',
});
assert.deepStrictEqual(appMetadataController.store.getState(), initState);
});

it('sets default state and does not modify it', async () => {
const appMetadataController = new AppMetadataController({
state: {},
});
assert.deepStrictEqual(
appMetadataController.store.getState(),
EXPECTED_DEFAULT_STATE,
);
});

it('sets default state and does not modify it if options version parameters match respective default values', async () => {
const appMetadataController = new AppMetadataController({
state: {},
currentMigrationVersion: 0,
currentAppVersion: '',
});
assert.deepStrictEqual(
appMetadataController.store.getState(),
EXPECTED_DEFAULT_STATE,
);
});

it('updates the currentAppVersion state property if options.currentAppVersion does not match the default value', async () => {
const appMetadataController = new AppMetadataController({
state: {},
currentMigrationVersion: 0,
currentAppVersion: '1',
});
assert.deepStrictEqual(appMetadataController.store.getState(), {
...EXPECTED_DEFAULT_STATE,
currentAppVersion: '1',
});
});

it('updates the currentAppVersion and previousAppVersion state properties if options.currentAppVersion, currentAppVersion and previousAppVersion are all different', async () => {
const appMetadataController = new AppMetadataController({
state: {
currentAppVersion: '2',
previousAppVersion: '1',
},
currentAppVersion: '3',
currentMigrationVersion: 0,
});
assert.deepStrictEqual(appMetadataController.store.getState(), {
...EXPECTED_DEFAULT_STATE,
currentAppVersion: '3',
previousAppVersion: '2',
});
});

it('updates the currentMigrationVersion state property if the currentMigrationVersion param does not match the default value', async () => {
const appMetadataController = new AppMetadataController({
state: {},
currentMigrationVersion: 1,
});
assert.deepStrictEqual(appMetadataController.store.getState(), {
...EXPECTED_DEFAULT_STATE,
currentMigrationVersion: 1,
});
});

it('updates the currentMigrationVersion and previousMigrationVersion state properties if the currentMigrationVersion param, the currentMigrationVersion state property and the previousMigrationVersion state property are all different', async () => {
const appMetadataController = new AppMetadataController({
state: {
currentMigrationVersion: 2,
previousMigrationVersion: 1,
},
currentMigrationVersion: 3,
});
assert.deepStrictEqual(appMetadataController.store.getState(), {
...EXPECTED_DEFAULT_STATE,
currentMigrationVersion: 3,
previousMigrationVersion: 2,
});
});
});
});
99 changes: 99 additions & 0 deletions app/scripts/controllers/app-metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import EventEmitter from 'events';
import { ObservableStore } from '@metamask/obs-store';

/**
* The state of the AppMetadataController
*/
export type AppMetadataControllerState = {
currentAppVersion: string;
previousAppVersion: string;
previousMigrationVersion: number;
currentMigrationVersion: number;
};

/**
* The options that NetworkController takes.
*/
export type AppMetadataControllerOptions = {
currentMigrationVersion?: number;
currentAppVersion?: string;
state?: Partial<AppMetadataControllerState>;
};

const defaultState: AppMetadataControllerState = {
currentAppVersion: '',
previousAppVersion: '',
previousMigrationVersion: 0,
currentMigrationVersion: 0,
};

/**
* The AppMetadata controller stores metadata about the current extension instance,
* including the currently and previously installed versions, and the most recently
* run migration.
*
*/
export default class AppMetadataController extends EventEmitter {
/**
* Observable store containing controller data.
*/
store: ObservableStore<AppMetadataControllerState>;

/**
* Constructs a AppMetadata controller.
*
* @param options - the controller options
* @param options.state - Initial controller state.
* @param options.currentMigrationVersion
* @param options.currentAppVersion
*/
constructor({
currentAppVersion = '',
currentMigrationVersion = 0,
state = {},
}: AppMetadataControllerOptions) {
super();

this.store = new ObservableStore({
...defaultState,
...state,
});

this.#maybeUpdateAppVersion(currentAppVersion);

this.#maybeUpdateMigrationVersion(currentMigrationVersion);
}

/**
* Updates the currentAppVersion in state, and sets the previousAppVersion to the old currentAppVersion.
*
* @param maybeNewAppVersion
*/
#maybeUpdateAppVersion(maybeNewAppVersion: string): void {
const oldCurrentAppVersion = this.store.getState().currentAppVersion;

if (maybeNewAppVersion !== oldCurrentAppVersion) {
this.store.updateState({
currentAppVersion: maybeNewAppVersion,
previousAppVersion: oldCurrentAppVersion,
});
}
}

/**
* Updates the migrationVersion in state.
*
* @param maybeNewMigrationVersion
*/
#maybeUpdateMigrationVersion(maybeNewMigrationVersion: number): void {
const oldCurrentMigrationVersion =
this.store.getState().currentMigrationVersion;

if (maybeNewMigrationVersion !== oldCurrentMigrationVersion) {
this.store.updateState({
previousMigrationVersion: oldCurrentMigrationVersion,
currentMigrationVersion: maybeNewMigrationVersion,
});
}
}
}
Loading