From 6d3a56aebc52c27005bc0b923ed5a7fdb28a251b Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Fri, 13 Sep 2024 08:04:51 -0700 Subject: [PATCH 1/2] feat(migration): enable token auto-detection when basic functionality is on (#27110) Cherry picks https://github.com/MetaMask/metamask-extension/pull/26406 to 12.3.0 Co-authored-by: sahar-fehri --- app/scripts/migrations/125.1.test.ts | 107 ++++++++++++++++++ app/scripts/migrations/125.1.ts | 50 ++++++++ app/scripts/migrations/index.js | 1 + ...rs-after-init-opt-in-background-state.json | 2 +- .../errors-after-init-opt-in-ui-state.json | 2 +- .../multichain/asset-picker-send.spec.ts | 13 ++- 6 files changed, 171 insertions(+), 4 deletions(-) create mode 100644 app/scripts/migrations/125.1.test.ts create mode 100644 app/scripts/migrations/125.1.ts diff --git a/app/scripts/migrations/125.1.test.ts b/app/scripts/migrations/125.1.test.ts new file mode 100644 index 000000000000..eb00db9d1e07 --- /dev/null +++ b/app/scripts/migrations/125.1.test.ts @@ -0,0 +1,107 @@ +import { migrate, version } from './125.1'; + +const oldVersion = 125; + +describe(`migration #${version}`, () => { + afterEach(() => jest.resetAllMocks()); + + it('updates the version metadata', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: {}, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.meta).toStrictEqual({ version }); + }); + + it('Gracefully handles empty/undefined PreferencesController', async () => { + for (const PreferencesController of [{}, undefined, null, 1, '', []]) { + const oldStorage = { + meta: { version: oldVersion }, + data: { PreferencesController }, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.data.TxController).toStrictEqual(undefined); + } + }); + + it('Enables token autodetection when basic functionality is on', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PreferencesController: { + useExternalServices: true, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toEqual({ + PreferencesController: { + useExternalServices: true, + useTokenDetection: true, + }, + }); + }); + + it('Does not enable token autodetection when basic functionality is off', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PreferencesController: { + useExternalServices: false, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toEqual({ + PreferencesController: { + useExternalServices: false, + }, + }); + }); + + it('Removes showTokenAutodetectModalOnUpgrade from the app metadata controller', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + AppMetadataController: { + previousMigrationVersion: oldVersion, + currentMigrationVersion: version, + showTokenAutodetectModalOnUpgrade: null, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toEqual({ + AppMetadataController: { + previousMigrationVersion: oldVersion, + currentMigrationVersion: version, + }, + }); + }); + + it('Does nothing if showTokenAutodetectModalOnUpgrade is not in the app metadata controller', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + AppMetadataController: { + previousMigrationVersion: oldVersion, + currentMigrationVersion: version, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toEqual({ + AppMetadataController: { + previousMigrationVersion: oldVersion, + currentMigrationVersion: version, + }, + }); + }); +}); diff --git a/app/scripts/migrations/125.1.ts b/app/scripts/migrations/125.1.ts new file mode 100644 index 000000000000..d3c975a78a11 --- /dev/null +++ b/app/scripts/migrations/125.1.ts @@ -0,0 +1,50 @@ +import { hasProperty, isObject } from '@metamask/utils'; +import { cloneDeep } from 'lodash'; + +type VersionedData = { + meta: { version: number }; + data: Record; +}; + +export const version = 125.1; + +/** + * This migration enables token auto-detection if the basic functionality toggle is on. + * + * It also removes an unused property `showTokenAutodetectModalOnUpgrade` from the app metadata controller. + * + * @param originalVersionedData - Versioned MetaMask extension state, exactly + * what we persist to dist. + * @param originalVersionedData.meta - State metadata. + * @param originalVersionedData.meta.version - The current state version. + * @param originalVersionedData.data - The persisted MetaMask state, keyed by + * controller. + * @returns Updated versioned MetaMask extension state. + */ +export async function migrate( + originalVersionedData: VersionedData, +): Promise { + const versionedData = cloneDeep(originalVersionedData); + versionedData.meta.version = version; + transformState(versionedData.data); + return versionedData; +} + +function transformState(state: Record) { + if ( + hasProperty(state, 'PreferencesController') && + isObject(state.PreferencesController) && + state.PreferencesController.useExternalServices === true + ) { + state.PreferencesController.useTokenDetection = true; + } + + if ( + hasProperty(state, 'AppMetadataController') && + isObject(state.AppMetadataController) + ) { + delete state.AppMetadataController.showTokenAutodetectModalOnUpgrade; + } + + return state; +} diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index 0146779d408f..bb64ec957f75 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -144,6 +144,7 @@ const migrations = [ require('./123'), require('./124'), require('./125'), + require('./125.1'), ]; export default migrations; diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json index 7a4d6335dab5..2d2c362b30f8 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json @@ -175,7 +175,7 @@ "dismissSeedBackUpReminder": true, "useMultiAccountBalanceChecker": true, "useSafeChainsListValidation": "boolean", - "useTokenDetection": false, + "useTokenDetection": true, "useNftDetection": false, "use4ByteResolution": true, "useCurrencyRateCheck": true, diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json index c74b7f35c85d..d5ff2eecc5eb 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json @@ -109,7 +109,7 @@ "dismissSeedBackUpReminder": true, "useMultiAccountBalanceChecker": true, "useSafeChainsListValidation": true, - "useTokenDetection": false, + "useTokenDetection": true, "useNftDetection": false, "useCurrencyRateCheck": true, "useRequestQueue": true, diff --git a/test/e2e/tests/multichain/asset-picker-send.spec.ts b/test/e2e/tests/multichain/asset-picker-send.spec.ts index 8358e8242f69..5accb14c6074 100644 --- a/test/e2e/tests/multichain/asset-picker-send.spec.ts +++ b/test/e2e/tests/multichain/asset-picker-send.spec.ts @@ -34,6 +34,15 @@ describe('AssetPickerSendFlow @no-mmi', function () { async ({ driver }: { driver: Driver }) => { await unlockWallet(driver); + // Disable token auto detection + await driver.openNewURL( + `${driver.extensionUrl}/home.html#settings/security`, + ); + await driver.clickElement( + '[data-testid="autoDetectTokens"] .toggle-button', + ); + await driver.navigate(); + // Open the send flow openActionMenuAndStartSendFlow(driver); @@ -72,13 +81,13 @@ describe('AssetPickerSendFlow @no-mmi', function () { assert.equal(tokenListSecondaryValue, '$250,000.00'); - // Search for BNB + // Search for CHZ const searchInputField = await driver.waitForSelector( '[data-testid="asset-picker-modal-search-input"]', ); await searchInputField.sendKeys('CHZ'); - // check that BNB is disabled + // check that CHZ is disabled const [, tkn] = await driver.findElements( '[data-testid="multichain-token-list-button"]', ); From 88b8ef114551b69ad37b39ef7bed048c7241cc41 Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Mon, 16 Sep 2024 17:14:20 +0200 Subject: [PATCH 2/2] fix(cherry-pick): resolve path-to-regexp to v1.9.0 to resolve GHSA-9wv6-86v2-598j (#27113) (#27159) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** This permanently fixes https://github.com/advisories/GHSA-9wv6-86v2-598j by resolving that package to a recently released version that does not having breaking changes and where the security vulnerability is resolved. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/27159?quickstart=1) ## **Related issues** Fixes: - https://github.com/advisories/GHSA-9wv6-86v2-598j ## **Manual testing steps** N/A ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. Co-authored-by: Dan J Miller Co-authored-by: MetaMask Bot --- .yarnrc.yml | 10 ---------- lavamoat/browserify/beta/policy.json | 6 +++--- lavamoat/browserify/flask/policy.json | 6 +++--- lavamoat/browserify/main/policy.json | 6 +++--- lavamoat/browserify/mmi/policy.json | 6 +++--- package.json | 3 ++- yarn.lock | 22 ++++------------------ 7 files changed, 18 insertions(+), 41 deletions(-) diff --git a/.yarnrc.yml b/.yarnrc.yml index 5d4aa7cd4e73..252333917781 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -43,16 +43,6 @@ npmAuditIgnoreAdvisories: # not appear to be used. - 1092461 - # Issue: path-to-regexp outputs backtracking regular expressions - # URL: https://github.com/advisories/GHSA-9wv6-86v2-598j - # path-to-regexp is used in react-router v5.1.2, which we use. However, the - # vulnerability in path-to-regexp could only be exploited within react-router - # if malicious properties were passed to react-router components or methods - # explicitly from our code. As such, this vulneratibility cannot be exploited - # by an external / malicious actor. Meanwhile, once we update to v6+, - # path-to-regexp will no longer be used. - - 1099518 - # Temp fix for https://github.com/MetaMask/metamask-extension/pull/16920 for the sake of 11.7.1 hotfix # This will be removed in this ticket https://github.com/MetaMask/metamask-extension/issues/22299 - 'ts-custom-error (deprecation)' diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index eb3caba43c21..605c88c89ea1 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -5552,7 +5552,7 @@ "react-router-dom>react-router>mini-create-react-context": true, "react-router-dom>tiny-invariant": true, "react-router-dom>tiny-warning": true, - "sinon>nise>path-to-regexp": true + "serve-handler>path-to-regexp": true } }, "react-router-dom>react-router>history": { @@ -5702,9 +5702,9 @@ "process": true } }, - "sinon>nise>path-to-regexp": { + "serve-handler>path-to-regexp": { "packages": { - "sinon>nise>path-to-regexp>isarray": true + "serve-handler>path-to-regexp>isarray": true } }, "stream-browserify": { diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index eb3caba43c21..605c88c89ea1 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -5552,7 +5552,7 @@ "react-router-dom>react-router>mini-create-react-context": true, "react-router-dom>tiny-invariant": true, "react-router-dom>tiny-warning": true, - "sinon>nise>path-to-regexp": true + "serve-handler>path-to-regexp": true } }, "react-router-dom>react-router>history": { @@ -5702,9 +5702,9 @@ "process": true } }, - "sinon>nise>path-to-regexp": { + "serve-handler>path-to-regexp": { "packages": { - "sinon>nise>path-to-regexp>isarray": true + "serve-handler>path-to-regexp>isarray": true } }, "stream-browserify": { diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index eb3caba43c21..605c88c89ea1 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -5552,7 +5552,7 @@ "react-router-dom>react-router>mini-create-react-context": true, "react-router-dom>tiny-invariant": true, "react-router-dom>tiny-warning": true, - "sinon>nise>path-to-regexp": true + "serve-handler>path-to-regexp": true } }, "react-router-dom>react-router>history": { @@ -5702,9 +5702,9 @@ "process": true } }, - "sinon>nise>path-to-regexp": { + "serve-handler>path-to-regexp": { "packages": { - "sinon>nise>path-to-regexp>isarray": true + "serve-handler>path-to-regexp>isarray": true } }, "stream-browserify": { diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 465b5dcf621b..96ba8b467983 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -5620,7 +5620,7 @@ "react-router-dom>react-router>mini-create-react-context": true, "react-router-dom>tiny-invariant": true, "react-router-dom>tiny-warning": true, - "sinon>nise>path-to-regexp": true + "serve-handler>path-to-regexp": true } }, "react-router-dom>react-router>history": { @@ -5770,9 +5770,9 @@ "process": true } }, - "sinon>nise>path-to-regexp": { + "serve-handler>path-to-regexp": { "packages": { - "sinon>nise>path-to-regexp>isarray": true + "serve-handler>path-to-regexp>isarray": true } }, "stream-browserify": { diff --git a/package.json b/package.json index be00bf7c6c65..c95b55be0b83 100644 --- a/package.json +++ b/package.json @@ -265,7 +265,8 @@ "@metamask/snaps-controllers@npm:^9.4.0": "patch:@metamask/snaps-controllers@npm%3A9.4.0#~/.yarn/patches/@metamask-snaps-controllers-npm-9.4.0-7c3abbbea6.patch", "@metamask/nonce-tracker@npm:^5.0.0": "patch:@metamask/nonce-tracker@npm%3A5.0.0#~/.yarn/patches/@metamask-nonce-tracker-npm-5.0.0-d81478218e.patch", "@metamask/keyring-controller@npm:^17.1.0": "patch:@metamask/keyring-controller@npm%3A17.1.1#~/.yarn/patches/@metamask-keyring-controller-npm-17.1.1-098cb41930.patch", - "@trezor/connect-web@npm:^9.1.11": "patch:@trezor/connect-web@npm%3A9.3.0#~/.yarn/patches/@trezor-connect-web-npm-9.3.0-040ab10d9a.patch" + "@trezor/connect-web@npm:^9.1.11": "patch:@trezor/connect-web@npm%3A9.3.0#~/.yarn/patches/@trezor-connect-web-npm-9.3.0-040ab10d9a.patch", + "path-to-regexp": "1.9.0" }, "dependencies": { "@babel/runtime": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", diff --git a/yarn.lock b/yarn.lock index d0660f4d3233..c67922eaa24d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28708,26 +28708,12 @@ __metadata: languageName: node linkType: hard -"path-to-regexp@npm:0.1.7": - version: 0.1.7 - resolution: "path-to-regexp@npm:0.1.7" - checksum: 10/701c99e1f08e3400bea4d701cf6f03517474bb1b608da71c78b1eb261415b645c5670dfae49808c89e12cea2dccd113b069f040a80de012da0400191c6dbd1c8 - languageName: node - linkType: hard - -"path-to-regexp@npm:2.2.1": - version: 2.2.1 - resolution: "path-to-regexp@npm:2.2.1" - checksum: 10/1a7125f8c1b5904d556a29722333219df4aa779039e903efe2fbfe0cc3ae9246672846fc8ad285664020b70e434347e0bc9af691fd7d61df8eaa7b018dcd56fb - languageName: node - linkType: hard - -"path-to-regexp@npm:^1.7.0": - version: 1.7.0 - resolution: "path-to-regexp@npm:1.7.0" +"path-to-regexp@npm:1.9.0": + version: 1.9.0 + resolution: "path-to-regexp@npm:1.9.0" dependencies: isarray: "npm:0.0.1" - checksum: 10/7e1275a34fcfed7ba9d0d82ea7149f0c87d8c941c9b34109ab455cceb783b6387ce9275deeb6519eb0f880777a44bcb387cd579d3bb0cfbf4e7fe93c0e3b1a69 + checksum: 10/67f0f4823f7aab356523d93a83f9f8222bdd119fa0b27a8f8b587e8e6c9825294bb4ccd16ae619def111ff3fe5d15ff8f658cdd3b0d58b9c882de6fd15bc1b76 languageName: node linkType: hard