From 9e42c34699f5566ee88c80590b651d476c926cfb Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Thu, 19 Dec 2024 17:10:49 +0000 Subject: [PATCH 1/8] Version v12.9.3 --- CHANGELOG.md | 5 ++++- package.json | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eda15db00496..65ffb876707e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [12.9.3] + ## [12.9.2] ### Changed - Display the "Amount" row within the advanced view of contract interaction confirmations, and whenever the amount being sent differs from the "You Send" row of the transaction simulation information by more than 5% ([#29131](https://github.com/MetaMask/metamask-extension/pull/29131)) @@ -5487,7 +5489,8 @@ Update styles and spacing on the critical error page ([#20350](https://github.c - Added the ability to restore accounts from seed words. -[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v12.9.2...HEAD +[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v12.9.3...HEAD +[12.9.3]: https://github.com/MetaMask/metamask-extension/compare/v12.9.2...v12.9.3 [12.9.2]: https://github.com/MetaMask/metamask-extension/compare/v12.9.1...v12.9.2 [12.9.1]: https://github.com/MetaMask/metamask-extension/compare/v12.9.0...v12.9.1 [12.9.0]: https://github.com/MetaMask/metamask-extension/compare/v12.8.1...v12.9.0 diff --git a/package.json b/package.json index 446133b81a48..5b6de0a41550 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "metamask-crx", - "version": "12.9.2", + "version": "12.9.3", "private": true, "repository": { "type": "git", From 46562e0e21ee7e1f6322f1ab5c77b2c8e42d37a3 Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Thu, 19 Dec 2024 14:34:18 -0800 Subject: [PATCH 2/8] chore(cherry-pick): fix erc20 token balances showing 0 (#29365) cherry picks https://github.com/MetaMask/metamask-extension/pull/29361 to v12.9.3 --- ...-assets-controllers-patch-d6ed5f8213.patch | 61 ++++++ app/scripts/migrations/133.2.test.ts | 185 ++++++++++++++++++ app/scripts/migrations/133.2.ts | 53 +++++ app/scripts/migrations/index.js | 1 + package.json | 2 +- yarn.lock | 43 +++- 6 files changed, 342 insertions(+), 3 deletions(-) create mode 100644 .yarn/patches/@metamask-assets-controllers-patch-d6ed5f8213.patch create mode 100644 app/scripts/migrations/133.2.test.ts create mode 100644 app/scripts/migrations/133.2.ts diff --git a/.yarn/patches/@metamask-assets-controllers-patch-d6ed5f8213.patch b/.yarn/patches/@metamask-assets-controllers-patch-d6ed5f8213.patch new file mode 100644 index 000000000000..02e6d3f694e5 --- /dev/null +++ b/.yarn/patches/@metamask-assets-controllers-patch-d6ed5f8213.patch @@ -0,0 +1,61 @@ +diff --git a/dist/multicall.cjs b/dist/multicall.cjs +index bf9aa5e86573fc1651f421cc0b64f5af121c3ab2..43a0531ed86cd3ee1774dcda3f990dd40f7f52de 100644 +--- a/dist/multicall.cjs ++++ b/dist/multicall.cjs +@@ -342,9 +342,22 @@ const multicallOrFallback = async (calls, chainId, provider, maxCallsPerMultical + return []; + } + const multicallAddress = MULTICALL_CONTRACT_BY_CHAINID[chainId]; +- return await (multicallAddress +- ? multicall(calls, multicallAddress, provider, maxCallsPerMulticall) +- : fallback(calls, maxCallsParallel)); ++ if (multicallAddress) { ++ try { ++ return await multicall(calls, multicallAddress, provider, maxCallsPerMulticall); ++ } ++ catch (error) { ++ // Fallback only on revert ++ // https://docs.ethers.org/v5/troubleshooting/errors/#help-CALL_EXCEPTION ++ if (!error || ++ typeof error !== 'object' || ++ !('code' in error) || ++ error.code !== 'CALL_EXCEPTION') { ++ throw error; ++ } ++ } ++ } ++ return await fallback(calls, maxCallsParallel); + }; + exports.multicallOrFallback = multicallOrFallback; + //# sourceMappingURL=multicall.cjs.map +\ No newline at end of file +diff --git a/dist/multicall.mjs b/dist/multicall.mjs +index 8fbe0112303d5df1d868e0357a9d31e43a3b6cf9..860dfdbddd813659cb2be5f7faed5d4016db5966 100644 +--- a/dist/multicall.mjs ++++ b/dist/multicall.mjs +@@ -339,8 +339,21 @@ export const multicallOrFallback = async (calls, chainId, provider, maxCallsPerM + return []; + } + const multicallAddress = MULTICALL_CONTRACT_BY_CHAINID[chainId]; +- return await (multicallAddress +- ? multicall(calls, multicallAddress, provider, maxCallsPerMulticall) +- : fallback(calls, maxCallsParallel)); ++ if (multicallAddress) { ++ try { ++ return await multicall(calls, multicallAddress, provider, maxCallsPerMulticall); ++ } ++ catch (error) { ++ // Fallback only on revert ++ // https://docs.ethers.org/v5/troubleshooting/errors/#help-CALL_EXCEPTION ++ if (!error || ++ typeof error !== 'object' || ++ !('code' in error) || ++ error.code !== 'CALL_EXCEPTION') { ++ throw error; ++ } ++ } ++ } ++ return await fallback(calls, maxCallsParallel); + }; + //# sourceMappingURL=multicall.mjs.map +\ No newline at end of file diff --git a/app/scripts/migrations/133.2.test.ts b/app/scripts/migrations/133.2.test.ts new file mode 100644 index 000000000000..18251d8f4b2b --- /dev/null +++ b/app/scripts/migrations/133.2.test.ts @@ -0,0 +1,185 @@ +import { migrate, version } from './133.2'; + +const oldVersion = 133.1; + +describe(`migration #${version}`, () => { + it('updates the version metadata', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: {}, + }; + const newStorage = await migrate(oldStorage); + expect(newStorage.meta).toStrictEqual({ version }); + }); + + it('does nothing if theres no tokens controller state defined', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: {}, + }; + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('does nothing if theres empty tokens controller state', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + TokensController: {}, + }, + }; + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('does nothing if theres empty tokens controller state for allTokens', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + TokensController: { + allTokens: {}, + }, + }, + }; + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('does nothing if theres empty tokens controller state for mainnet', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + TokensController: { + allTokens: { + '0x1': {}, + }, + }, + }, + }; + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('Does nothing if theres no tokens with empty address', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + TokensController: { + allTokens: { + '0x1': { + '0x123': [ + { address: '0x1', symbol: 'TOKEN1', decimals: 18 }, + { address: '0x2', symbol: 'TOKEN2', decimals: 18 }, + ], + '0x123456': [ + { address: '0x3', symbol: 'TOKEN3', decimals: 18 }, + { address: '0x4', symbol: 'TOKEN4', decimals: 18 }, + ], + }, + }, + }, + }, + }; + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('Removes tokens with empty address', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + TokensController: { + allTokens: { + '0x1': { + '0x123': [ + { + address: '0x0000000000000000000000000000000000000000', + symbol: 'eth', + decimals: 18, + }, + { address: '0x2', symbol: 'TOKEN2', decimals: 18 }, + ], + }, + }, + }, + }, + }; + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual({ + TokensController: { + allTokens: { + '0x1': { + '0x123': [{ address: '0x2', symbol: 'TOKEN2', decimals: 18 }], + }, + }, + }, + }); + }); + + it('Removes tokens with empty address across multiple accounts', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + TokensController: { + allTokens: { + '0x1': { + '0x123': [ + { + address: '0x0000000000000000000000000000000000000000', + symbol: 'eth', + decimals: 18, + }, + { address: '0x2', symbol: 'TOKEN2', decimals: 18 }, + ], + '0x456': [ + { + address: '0x0000000000000000000000000000000000000000', + symbol: 'eth', + decimals: 18, + }, + { address: '0x3', symbol: 'TOKEN3', decimals: 18 }, + ], + '0x789': [{ address: '0x4', symbol: 'TOKEN4', decimals: 18 }], + }, + }, + }, + }, + }; + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual({ + TokensController: { + allTokens: { + '0x1': { + '0x123': [{ address: '0x2', symbol: 'TOKEN2', decimals: 18 }], + '0x456': [{ address: '0x3', symbol: 'TOKEN3', decimals: 18 }], + '0x789': [{ address: '0x4', symbol: 'TOKEN4', decimals: 18 }], + }, + }, + }, + }); + }); + + it('Does not change state on chains other than mainnet', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + TokensController: { + allTokens: { + '0x999': { + '0x123': [ + { + address: '0x0000000000000000000000000000000000000000', + symbol: 'eth', + decimals: 18, + }, + { address: '0x2', symbol: 'TOKEN2', decimals: 18 }, + ], + }, + }, + }, + }, + }; + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); +}); diff --git a/app/scripts/migrations/133.2.ts b/app/scripts/migrations/133.2.ts new file mode 100644 index 000000000000..6ad8ff888cfd --- /dev/null +++ b/app/scripts/migrations/133.2.ts @@ -0,0 +1,53 @@ +import { hasProperty, isObject } from '@metamask/utils'; +import { cloneDeep } from 'lodash'; + +type VersionedData = { + meta: { version: number }; + data: Record; +}; + +export const version = 133.2; + +/** + * This migration removes tokens on mainnet with the + * zero address, since this is not a valid erc20 token. + * + * @param originalVersionedData - Versioned MetaMask extension state, exactly + * what we persist to disk. + * @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): void { + if ( + !hasProperty(state, 'TokensController') || + !isObject(state.TokensController) || + !isObject(state.TokensController.allTokens) + ) { + return; + } + + const chainIds = ['0x1']; + + for (const chainId of chainIds) { + const allTokensOnChain = state.TokensController.allTokens[chainId]; + + if (isObject(allTokensOnChain)) { + for (const [account, tokens] of Object.entries(allTokensOnChain)) { + if (Array.isArray(tokens)) { + allTokensOnChain[account] = tokens.filter( + (token) => + token?.address !== '0x0000000000000000000000000000000000000000', + ); + } + } + } + } +} diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index fbee63b7f7f2..3783cfa0b8bd 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -156,6 +156,7 @@ const migrations = [ require('./132'), require('./133'), require('./133.1'), + require('./133.2'), ]; export default migrations; diff --git a/package.json b/package.json index 5b6de0a41550..790a47a6adef 100644 --- a/package.json +++ b/package.json @@ -286,7 +286,7 @@ "@metamask/address-book-controller": "^6.0.0", "@metamask/announcement-controller": "^7.0.0", "@metamask/approval-controller": "^7.0.0", - "@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A45.1.0#~/.yarn/patches/@metamask-assets-controllers-npm-45.1.0-d914c453f0.patch", + "@metamask/assets-controllers": "patch:@metamask/assets-controllers@patch%3A@metamask/assets-controllers@npm%253A45.1.0%23~/.yarn/patches/@metamask-assets-controllers-npm-45.1.0-d914c453f0.patch%3A%3Aversion=45.1.0&hash=cfcadc#~/.yarn/patches/@metamask-assets-controllers-patch-d6ed5f8213.patch", "@metamask/base-controller": "^7.0.0", "@metamask/bitcoin-wallet-snap": "^0.8.2", "@metamask/browser-passworder": "^4.3.0", diff --git a/yarn.lock b/yarn.lock index 70482a79a761..7eaa7689b391 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4985,7 +4985,7 @@ __metadata: languageName: node linkType: hard -"@metamask/assets-controllers@patch:@metamask/assets-controllers@npm%3A45.1.0#~/.yarn/patches/@metamask-assets-controllers-npm-45.1.0-d914c453f0.patch": +"@metamask/assets-controllers@patch:@metamask/assets-controllers@npm%3A45.1.0#~/.yarn/patches/@metamask-assets-controllers-npm-45.1.0-d914c453f0.patch::version=45.1.0&hash=cfcadc": version: 45.1.0 resolution: "@metamask/assets-controllers@patch:@metamask/assets-controllers@npm%3A45.1.0#~/.yarn/patches/@metamask-assets-controllers-npm-45.1.0-d914c453f0.patch::version=45.1.0&hash=cfcadc" dependencies: @@ -5024,6 +5024,45 @@ __metadata: languageName: node linkType: hard +"@metamask/assets-controllers@patch:@metamask/assets-controllers@patch%3A@metamask/assets-controllers@npm%253A45.1.0%23~/.yarn/patches/@metamask-assets-controllers-npm-45.1.0-d914c453f0.patch%3A%3Aversion=45.1.0&hash=cfcadc#~/.yarn/patches/@metamask-assets-controllers-patch-d6ed5f8213.patch": + version: 45.1.0 + resolution: "@metamask/assets-controllers@patch:@metamask/assets-controllers@patch%3A@metamask/assets-controllers@npm%253A45.1.0%23~/.yarn/patches/@metamask-assets-controllers-npm-45.1.0-d914c453f0.patch%3A%3Aversion=45.1.0&hash=cfcadc#~/.yarn/patches/@metamask-assets-controllers-patch-d6ed5f8213.patch::version=45.1.0&hash=4e79dd" + dependencies: + "@ethereumjs/util": "npm:^8.1.0" + "@ethersproject/abi": "npm:^5.7.0" + "@ethersproject/address": "npm:^5.7.0" + "@ethersproject/bignumber": "npm:^5.7.0" + "@ethersproject/contracts": "npm:^5.7.0" + "@ethersproject/providers": "npm:^5.7.0" + "@metamask/abi-utils": "npm:^2.0.3" + "@metamask/base-controller": "npm:^7.0.2" + "@metamask/contract-metadata": "npm:^2.4.0" + "@metamask/controller-utils": "npm:^11.4.3" + "@metamask/eth-query": "npm:^4.0.0" + "@metamask/metamask-eth-abis": "npm:^3.1.1" + "@metamask/polling-controller": "npm:^12.0.1" + "@metamask/rpc-errors": "npm:^7.0.1" + "@metamask/utils": "npm:^10.0.0" + "@types/bn.js": "npm:^5.1.5" + "@types/uuid": "npm:^8.3.0" + async-mutex: "npm:^0.5.0" + bn.js: "npm:^5.2.1" + cockatiel: "npm:^3.1.2" + immer: "npm:^9.0.6" + lodash: "npm:^4.17.21" + multiformats: "npm:^13.1.0" + single-call-balance-checker-abi: "npm:^1.0.0" + uuid: "npm:^8.3.2" + peerDependencies: + "@metamask/accounts-controller": ^20.0.0 + "@metamask/approval-controller": ^7.0.0 + "@metamask/keyring-controller": ^19.0.0 + "@metamask/network-controller": ^22.0.0 + "@metamask/preferences-controller": ^15.0.0 + checksum: 10/26260472e67d0995b3730870fed99ba081c421ea64e8ca70f02ca8184fb9350fd2c607b75f45507743ba73d7336e831cc55a7aaf9a32f569a58eb7abb9275451 + languageName: node + linkType: hard + "@metamask/auto-changelog@npm:^2.1.0": version: 2.6.1 resolution: "@metamask/auto-changelog@npm:2.6.1" @@ -26596,7 +26635,7 @@ __metadata: "@metamask/announcement-controller": "npm:^7.0.0" "@metamask/api-specs": "npm:^0.9.3" "@metamask/approval-controller": "npm:^7.0.0" - "@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A45.1.0#~/.yarn/patches/@metamask-assets-controllers-npm-45.1.0-d914c453f0.patch" + "@metamask/assets-controllers": "patch:@metamask/assets-controllers@patch%3A@metamask/assets-controllers@npm%253A45.1.0%23~/.yarn/patches/@metamask-assets-controllers-npm-45.1.0-d914c453f0.patch%3A%3Aversion=45.1.0&hash=cfcadc#~/.yarn/patches/@metamask-assets-controllers-patch-d6ed5f8213.patch" "@metamask/auto-changelog": "npm:^2.1.0" "@metamask/base-controller": "npm:^7.0.0" "@metamask/bitcoin-wallet-snap": "npm:^0.8.2" From 317b923c2f250fca38bd5f4c60d167ff65bc569b Mon Sep 17 00:00:00 2001 From: OGPoyraz Date: Fri, 20 Dec 2024 12:04:25 +0100 Subject: [PATCH 3/8] chore: Cherry pick `29343` (#29376) This PR cherry picks https://github.com/MetaMask/metamask-extension/pull/29343 --- app/scripts/lib/ppom/ppom-util.test.ts | 50 +++++++++++++++++++++++++- app/scripts/lib/ppom/ppom-util.ts | 20 ++++++++++- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/app/scripts/lib/ppom/ppom-util.test.ts b/app/scripts/lib/ppom/ppom-util.test.ts index 8acb6dd9788c..d3ab62b09d88 100644 --- a/app/scripts/lib/ppom/ppom-util.test.ts +++ b/app/scripts/lib/ppom/ppom-util.test.ts @@ -10,7 +10,7 @@ import { SignatureController, SignatureRequest, } from '@metamask/signature-controller'; -import { Hex } from '@metamask/utils'; +import { Hex, JsonRpcRequest } from '@metamask/utils'; import { BlockaidReason, BlockaidResultType, @@ -22,6 +22,8 @@ import { AppStateController } from '../../controllers/app-state-controller'; import { generateSecurityAlertId, isChainSupported, + METHOD_SIGN_TYPED_DATA_V3, + METHOD_SIGN_TYPED_DATA_V4, updateSecurityAlertResponse, validateRequestWithPPOM, } from './ppom-util'; @@ -57,6 +59,10 @@ const TRANSACTION_PARAMS_MOCK_1: TransactionParams = { value: '0x123', }; +const SIGN_TYPED_DATA_PARAMS_MOCK_1 = '0x123'; +const SIGN_TYPED_DATA_PARAMS_MOCK_2 = + '{"primaryType":"Permit","domain":{},"types":{}}'; + const TRANSACTION_PARAMS_MOCK_2: TransactionParams = { ...TRANSACTION_PARAMS_MOCK_1, to: '0x456', @@ -259,6 +265,48 @@ describe('PPOM Utils', () => { ); }); + // @ts-expect-error This is missing from the Mocha type definitions + it.each([METHOD_SIGN_TYPED_DATA_V3, METHOD_SIGN_TYPED_DATA_V4])( + 'sanitizes request params if method is %s', + async (method: string) => { + const ppom = createPPOMMock(); + const ppomController = createPPOMControllerMock(); + + ppomController.usePPOM.mockImplementation( + (callback) => + // eslint-disable-next-line @typescript-eslint/no-explicit-any + callback(ppom as any) as any, + ); + + const firstTwoParams = [ + SIGN_TYPED_DATA_PARAMS_MOCK_1, + SIGN_TYPED_DATA_PARAMS_MOCK_2, + ]; + + const unwantedParams = [{}, undefined, 1, null]; + + const params = [...firstTwoParams, ...unwantedParams]; + + const request = { + ...REQUEST_MOCK, + method, + params, + } as unknown as JsonRpcRequest; + + await validateRequestWithPPOM({ + ...validateRequestWithPPOMOptionsBase, + ppomController, + request, + }); + + expect(ppom.validateJsonRpc).toHaveBeenCalledTimes(1); + expect(ppom.validateJsonRpc).toHaveBeenCalledWith({ + ...request, + params: firstTwoParams, + }); + }, + ); + it('updates response indicating chain is not supported', async () => { const ppomController = {} as PPOMController; const CHAIN_ID_UNSUPPORTED_MOCK = '0x2'; diff --git a/app/scripts/lib/ppom/ppom-util.ts b/app/scripts/lib/ppom/ppom-util.ts index d27ec6c8e505..6572aa3c3839 100644 --- a/app/scripts/lib/ppom/ppom-util.ts +++ b/app/scripts/lib/ppom/ppom-util.ts @@ -29,6 +29,8 @@ import { const { sentry } = global; const METHOD_SEND_TRANSACTION = 'eth_sendTransaction'; +export const METHOD_SIGN_TYPED_DATA_V3 = 'eth_signTypedData_v3'; +export const METHOD_SIGN_TYPED_DATA_V4 = 'eth_signTypedData_v4'; const SECURITY_ALERT_RESPONSE_ERROR = { result_type: BlockaidResultType.Errored, @@ -169,7 +171,7 @@ function normalizePPOMRequest( request, ) ) { - return request; + return sanitizeRequest(request); } const transactionParams = request.params[0]; @@ -181,6 +183,22 @@ function normalizePPOMRequest( }; } +function sanitizeRequest(request: JsonRpcRequest): JsonRpcRequest { + // This is a temporary fix to prevent a PPOM bypass + if ( + request.method === METHOD_SIGN_TYPED_DATA_V4 || + request.method === METHOD_SIGN_TYPED_DATA_V3 + ) { + if (Array.isArray(request.params)) { + return { + ...request, + params: request.params.slice(0, 2), + }; + } + } + return request; +} + function getErrorMessage(error: unknown) { if (error instanceof Error) { return `${error.name}: ${error.message}`; From 1712e287c0df901a475d8c4c4cbf6c20d1c30e28 Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Fri, 20 Dec 2024 17:25:52 +0000 Subject: [PATCH 4/8] fix (cherry-pick): remove reliance on transaction decode in confirmations #29341 (#29397) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Cherry-pick of #29341 for release 12.9.3. Difference to `main` is that redesigned transactions are disabled if the `Decode smart contracts` toggle is disabled. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/29397?quickstart=1) ## **Related issues** ## **Manual testing steps** ## **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/main/.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/main/.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. --- shared/lib/confirmation.utils.test.ts | 22 ++++++++++++++++ shared/lib/confirmation.utils.ts | 6 +++++ .../hooks/useDecodedTransactionData.test.ts | 26 +++++++++++++++++++ .../info/hooks/useDecodedTransactionData.ts | 12 ++++++++- .../hooks/useCurrentConfirmation.test.ts | 16 ++++++++++++ .../hooks/useCurrentConfirmation.ts | 3 +++ 6 files changed, 84 insertions(+), 1 deletion(-) diff --git a/shared/lib/confirmation.utils.test.ts b/shared/lib/confirmation.utils.test.ts index 552d78827a2e..add03a95813d 100644 --- a/shared/lib/confirmation.utils.test.ts +++ b/shared/lib/confirmation.utils.test.ts @@ -43,6 +43,17 @@ describe('confirmation.utils', () => { }), ).toBe(false); }); + + it('should return false if decoding is disabled', () => { + expect( + shouldUseRedesignForTransactions({ + transactionMetadataType: unsupportedTransactionType, + isRedesignedTransactionsUserSettingEnabled: true, // user setting enabled + isRedesignedConfirmationsDeveloperEnabled: false, // developer mode disabled + isDecodingEnabled: false, + }), + ).toBe(false); + }); }); describe('when developer mode is enabled', () => { @@ -93,6 +104,17 @@ describe('confirmation.utils', () => { }), ).toBe(false); }); + + it('should return false if decoding is disabled', () => { + expect( + shouldUseRedesignForTransactions({ + transactionMetadataType: unsupportedTransactionType, + isRedesignedTransactionsUserSettingEnabled: false, // user setting disabled + isRedesignedConfirmationsDeveloperEnabled: true, // developer mode enabled + isDecodingEnabled: false, + }), + ).toBe(false); + }); }); describe('when both user setting and developer mode are disabled', () => { diff --git a/shared/lib/confirmation.utils.ts b/shared/lib/confirmation.utils.ts index 24c5f258a5d0..1813211582be 100644 --- a/shared/lib/confirmation.utils.ts +++ b/shared/lib/confirmation.utils.ts @@ -37,11 +37,17 @@ export function shouldUseRedesignForTransactions({ transactionMetadataType, isRedesignedTransactionsUserSettingEnabled, isRedesignedConfirmationsDeveloperEnabled, + isDecodingEnabled, }: { transactionMetadataType?: TransactionType; isRedesignedTransactionsUserSettingEnabled: boolean; isRedesignedConfirmationsDeveloperEnabled: boolean; + isDecodingEnabled?: boolean; }): boolean { + if (isDecodingEnabled === false) { + return false; + } + return ( shouldUseRedesignForTransactionsUserMode( isRedesignedTransactionsUserSettingEnabled, diff --git a/ui/pages/confirmations/components/confirm/info/hooks/useDecodedTransactionData.test.ts b/ui/pages/confirmations/components/confirm/info/hooks/useDecodedTransactionData.test.ts index 32a711abf754..c12ce7025cb5 100644 --- a/ui/pages/confirmations/components/confirm/info/hooks/useDecodedTransactionData.test.ts +++ b/ui/pages/confirmations/components/confirm/info/hooks/useDecodedTransactionData.test.ts @@ -76,6 +76,32 @@ describe('useDecodedTransactionData', () => { expect(result).toStrictEqual({ pending: false, value: undefined }); }); + it('returns undefined if decode disabled', async () => { + decodeTransactionDataMock.mockResolvedValue(TRANSACTION_DECODE_SOURCIFY); + + const result = await runHook( + getMockConfirmStateForTransaction( + { + id: '123', + chainId: CHAIN_ID_MOCK, + type: TransactionType.contractInteraction, + status: TransactionStatus.unapproved, + txParams: { + data: TRANSACTION_DATA_UNISWAP, + to: CONTRACT_ADDRESS_MOCK, + } as TransactionParams, + }, + { + metamask: { + use4ByteResolution: false, + }, + }, + ), + ); + + expect(result).toStrictEqual({ pending: false, value: undefined }); + }); + it('returns the decoded data', async () => { decodeTransactionDataMock.mockResolvedValue(TRANSACTION_DECODE_SOURCIFY); diff --git a/ui/pages/confirmations/components/confirm/info/hooks/useDecodedTransactionData.ts b/ui/pages/confirmations/components/confirm/info/hooks/useDecodedTransactionData.ts index 5276e02eaad1..3486f16ed864 100644 --- a/ui/pages/confirmations/components/confirm/info/hooks/useDecodedTransactionData.ts +++ b/ui/pages/confirmations/components/confirm/info/hooks/useDecodedTransactionData.ts @@ -1,6 +1,7 @@ import { Hex } from '@metamask/utils'; import { TransactionMeta } from '@metamask/transaction-controller'; +import { useSelector } from 'react-redux'; import { AsyncResult, useAsyncResult, @@ -9,11 +10,13 @@ import { decodeTransactionData } from '../../../../../../store/actions'; import { DecodedTransactionDataResponse } from '../../../../../../../shared/types/transaction-decode'; import { useConfirmContext } from '../../../../context/confirm'; import { hasTransactionData } from '../../../../../../../shared/modules/transaction.utils'; +import { use4ByteResolutionSelector } from '../../../../../../selectors'; export function useDecodedTransactionData( transactionTypeFilter?: string, ): AsyncResult { const { currentConfirmation } = useConfirmContext(); + const isDecodeEnabled = useSelector(use4ByteResolutionSelector); const currentTransactionType = currentConfirmation?.type; const chainId = currentConfirmation?.chainId as Hex; @@ -23,6 +26,7 @@ export function useDecodedTransactionData( return useAsyncResult(async () => { if ( + !isDecodeEnabled || !hasTransactionData(transactionData) || !transactionTo || (transactionTypeFilter && @@ -36,5 +40,11 @@ export function useDecodedTransactionData( chainId, contractAddress, }); - }, [transactionData, transactionTo, chainId, contractAddress]); + }, [ + isDecodeEnabled, + transactionData, + transactionTo, + chainId, + contractAddress, + ]); } diff --git a/ui/pages/confirmations/hooks/useCurrentConfirmation.test.ts b/ui/pages/confirmations/hooks/useCurrentConfirmation.test.ts index 8c66873d448c..efd26d14da39 100644 --- a/ui/pages/confirmations/hooks/useCurrentConfirmation.test.ts +++ b/ui/pages/confirmations/hooks/useCurrentConfirmation.test.ts @@ -59,6 +59,7 @@ function buildState({ redesignedTransactionsEnabled, transaction, isRedesignedConfirmationsDeveloperEnabled, + isDecodingEnabled, }: { // eslint-disable-next-line @typescript-eslint/no-explicit-any message?: Partial; @@ -67,6 +68,7 @@ function buildState({ redesignedTransactionsEnabled?: boolean; transaction?: Partial; isRedesignedConfirmationsDeveloperEnabled?: boolean; + isDecodingEnabled?: boolean; }) { return { ...mockState, @@ -83,6 +85,7 @@ function buildState({ unapprovedPersonalMsgs: message ? { [message.id as string]: message } : {}, + use4ByteResolution: isDecodingEnabled ?? true, }, }; } @@ -290,6 +293,19 @@ describe('useCurrentConfirmation', () => { expect(currentConfirmation).toStrictEqual(TRANSACTION_MOCK); }); + it('returns undefined if transaction type correct and redesign enabled but decoding disabled', () => { + const currentConfirmation = runHook({ + pendingApprovals: [{ ...APPROVAL_MOCK, type: ApprovalType.Transaction }], + redesignedConfirmationsEnabled: true, + transaction: TRANSACTION_MOCK, + redesignedTransactionsEnabled: true, + isRedesignedConfirmationsDeveloperEnabled: false, + isDecodingEnabled: false, + }); + + expect(currentConfirmation).toBeUndefined(); + }); + describe('useCurrentConfirmation with env var', () => { beforeAll(() => { jest.resetModules(); diff --git a/ui/pages/confirmations/hooks/useCurrentConfirmation.ts b/ui/pages/confirmations/hooks/useCurrentConfirmation.ts index 1771f807de25..b2ccc5fd4594 100644 --- a/ui/pages/confirmations/hooks/useCurrentConfirmation.ts +++ b/ui/pages/confirmations/hooks/useCurrentConfirmation.ts @@ -11,6 +11,7 @@ import { getUnapprovedTransaction, oldestPendingConfirmationSelector, selectPendingApproval, + use4ByteResolutionSelector, } from '../../../selectors'; import { selectUnapprovedMessage } from '../../../selectors/signatures'; import { @@ -30,6 +31,7 @@ const useCurrentConfirmation = () => { const { id: paramsConfirmationId } = useParams<{ id: string }>(); const oldestPendingApproval = useSelector(oldestPendingConfirmationSelector); const confirmationId = paramsConfirmationId ?? oldestPendingApproval?.id; + const isDecodingEnabled = Boolean(useSelector(use4ByteResolutionSelector)); const isRedesignedSignaturesUserSettingEnabled = useSelector( getRedesignedConfirmationsEnabled, @@ -66,6 +68,7 @@ const useCurrentConfirmation = () => { transactionMetadataType: transactionMetadata?.type, isRedesignedTransactionsUserSettingEnabled, isRedesignedConfirmationsDeveloperEnabled, + isDecodingEnabled, }); const shouldUseRedesign = From 611f3bbbabda9dfbe2ae998e7e8153bcaa81b758 Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Fri, 20 Dec 2024 14:32:49 -0330 Subject: [PATCH 5/8] chore (cherry-pick): fix: nanoid audit issue (#29268) (#29398) Cherry pick 0e10bab6bc (#29268) to v12.9.3 Co-authored-by: Alejandro Garcia Anglada Co-authored-by: MetaMask Bot --- .../controllers/permissions/background-api.js | 2 +- app/scripts/metamask-controller.js | 2 +- lavamoat/browserify/beta/policy.json | 14 ++++++-------- lavamoat/browserify/flask/policy.json | 14 ++++++-------- lavamoat/browserify/main/policy.json | 14 ++++++-------- lavamoat/browserify/mmi/policy.json | 14 ++++++-------- lavamoat/build-system/policy.json | 2 +- package.json | 2 +- yarn.lock | 12 ++++++------ 9 files changed, 34 insertions(+), 42 deletions(-) diff --git a/app/scripts/controllers/permissions/background-api.js b/app/scripts/controllers/permissions/background-api.js index b778ff42385d..8a0942667f17 100644 --- a/app/scripts/controllers/permissions/background-api.js +++ b/app/scripts/controllers/permissions/background-api.js @@ -1,4 +1,4 @@ -import nanoid from 'nanoid'; +import { nanoid } from 'nanoid'; import { CaveatTypes, RestrictedMethods, diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index d60d937e1c3c..41687031aec5 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -47,7 +47,7 @@ import { rawChainData } from 'eth-chainlist'; import { MetaMaskKeyring as QRHardwareKeyring } from '@keystonehq/metamask-airgapped-keyring'; import EthQuery from '@metamask/eth-query'; import EthJSQuery from '@metamask/ethjs-query'; -import nanoid from 'nanoid'; +import { nanoid } from 'nanoid'; import { captureException } from '@sentry/browser'; import { AddressBookController } from '@metamask/address-book-controller'; import { diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index b3b118eb6bb8..ddd7640564ab 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -629,9 +629,9 @@ "console.info": true }, "packages": { - "@metamask/approval-controller>nanoid": true, "@metamask/base-controller": true, - "@metamask/rpc-errors": true + "@metamask/rpc-errors": true, + "nanoid": true } }, "@metamask/approval-controller>nanoid": { @@ -2044,11 +2044,11 @@ "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/json-rpc-engine": true, - "@metamask/permission-controller>nanoid": true, "@metamask/rpc-errors": true, "@metamask/utils": true, "deep-freeze-strict": true, - "immer": true + "immer": true, + "nanoid": true } }, "@metamask/permission-controller>nanoid": { @@ -2506,7 +2506,6 @@ "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, "@metamask/snaps-controllers>get-npm-tarball-url": true, - "@metamask/snaps-controllers>nanoid": true, "@metamask/snaps-controllers>readable-web-to-node-stream": true, "@metamask/snaps-controllers>tar-stream": true, "@metamask/snaps-rpc-methods": true, @@ -2517,6 +2516,7 @@ "browserify>browserify-zlib": true, "eslint>fast-deep-equal": true, "immer": true, + "nanoid": true, "readable-stream": true, "semver": true } @@ -4601,9 +4601,7 @@ }, "nanoid": { "globals": { - "crypto": true, - "msCrypto": true, - "navigator": true + "crypto.getRandomValues": true } }, "nock>debug": { diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index b3b118eb6bb8..ddd7640564ab 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -629,9 +629,9 @@ "console.info": true }, "packages": { - "@metamask/approval-controller>nanoid": true, "@metamask/base-controller": true, - "@metamask/rpc-errors": true + "@metamask/rpc-errors": true, + "nanoid": true } }, "@metamask/approval-controller>nanoid": { @@ -2044,11 +2044,11 @@ "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/json-rpc-engine": true, - "@metamask/permission-controller>nanoid": true, "@metamask/rpc-errors": true, "@metamask/utils": true, "deep-freeze-strict": true, - "immer": true + "immer": true, + "nanoid": true } }, "@metamask/permission-controller>nanoid": { @@ -2506,7 +2506,6 @@ "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, "@metamask/snaps-controllers>get-npm-tarball-url": true, - "@metamask/snaps-controllers>nanoid": true, "@metamask/snaps-controllers>readable-web-to-node-stream": true, "@metamask/snaps-controllers>tar-stream": true, "@metamask/snaps-rpc-methods": true, @@ -2517,6 +2516,7 @@ "browserify>browserify-zlib": true, "eslint>fast-deep-equal": true, "immer": true, + "nanoid": true, "readable-stream": true, "semver": true } @@ -4601,9 +4601,7 @@ }, "nanoid": { "globals": { - "crypto": true, - "msCrypto": true, - "navigator": true + "crypto.getRandomValues": true } }, "nock>debug": { diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index b3b118eb6bb8..ddd7640564ab 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -629,9 +629,9 @@ "console.info": true }, "packages": { - "@metamask/approval-controller>nanoid": true, "@metamask/base-controller": true, - "@metamask/rpc-errors": true + "@metamask/rpc-errors": true, + "nanoid": true } }, "@metamask/approval-controller>nanoid": { @@ -2044,11 +2044,11 @@ "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/json-rpc-engine": true, - "@metamask/permission-controller>nanoid": true, "@metamask/rpc-errors": true, "@metamask/utils": true, "deep-freeze-strict": true, - "immer": true + "immer": true, + "nanoid": true } }, "@metamask/permission-controller>nanoid": { @@ -2506,7 +2506,6 @@ "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, "@metamask/snaps-controllers>get-npm-tarball-url": true, - "@metamask/snaps-controllers>nanoid": true, "@metamask/snaps-controllers>readable-web-to-node-stream": true, "@metamask/snaps-controllers>tar-stream": true, "@metamask/snaps-rpc-methods": true, @@ -2517,6 +2516,7 @@ "browserify>browserify-zlib": true, "eslint>fast-deep-equal": true, "immer": true, + "nanoid": true, "readable-stream": true, "semver": true } @@ -4601,9 +4601,7 @@ }, "nanoid": { "globals": { - "crypto": true, - "msCrypto": true, - "navigator": true + "crypto.getRandomValues": true } }, "nock>debug": { diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index b4f5e137de6b..e9a50dd8057d 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -721,9 +721,9 @@ "console.info": true }, "packages": { - "@metamask/approval-controller>nanoid": true, "@metamask/base-controller": true, - "@metamask/rpc-errors": true + "@metamask/rpc-errors": true, + "nanoid": true } }, "@metamask/approval-controller>nanoid": { @@ -2136,11 +2136,11 @@ "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/json-rpc-engine": true, - "@metamask/permission-controller>nanoid": true, "@metamask/rpc-errors": true, "@metamask/utils": true, "deep-freeze-strict": true, - "immer": true + "immer": true, + "nanoid": true } }, "@metamask/permission-controller>nanoid": { @@ -2598,7 +2598,6 @@ "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, "@metamask/snaps-controllers>get-npm-tarball-url": true, - "@metamask/snaps-controllers>nanoid": true, "@metamask/snaps-controllers>readable-web-to-node-stream": true, "@metamask/snaps-controllers>tar-stream": true, "@metamask/snaps-rpc-methods": true, @@ -2609,6 +2608,7 @@ "browserify>browserify-zlib": true, "eslint>fast-deep-equal": true, "immer": true, + "nanoid": true, "readable-stream": true, "semver": true } @@ -4693,9 +4693,7 @@ }, "nanoid": { "globals": { - "crypto": true, - "msCrypto": true, - "navigator": true + "crypto.getRandomValues": true } }, "nock>debug": { diff --git a/lavamoat/build-system/policy.json b/lavamoat/build-system/policy.json index 5338922720ef..32f1b088ad71 100644 --- a/lavamoat/build-system/policy.json +++ b/lavamoat/build-system/policy.json @@ -6560,7 +6560,7 @@ "process.env.NODE_ENV": true }, "packages": { - "postcss>nanoid": true, + "nanoid": true, "postcss>picocolors": true, "postcss>source-map-js": true } diff --git a/package.json b/package.json index 790a47a6adef..4532335b3aa1 100644 --- a/package.json +++ b/package.json @@ -397,7 +397,7 @@ "loglevel": "^1.8.1", "lottie-web": "^5.12.2", "luxon": "^3.2.1", - "nanoid": "^2.1.6", + "nanoid": "^3.3.8", "pify": "^5.0.0", "promise-to-callback": "^1.0.0", "prop-types": "^15.6.1", diff --git a/yarn.lock b/yarn.lock index 7eaa7689b391..5ee17e2614d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -26910,7 +26910,7 @@ __metadata: mocha: "npm:^10.2.0" mocha-junit-reporter: "npm:^2.2.1" mockttp: "npm:^3.10.1" - nanoid: "npm:^2.1.6" + nanoid: "npm:^3.3.8" nock: "patch:nock@npm%3A13.5.4#~/.yarn/patches/nock-npm-13.5.4-2c4f77b249.patch" node-fetch: "npm:^2.6.1" nyc: "npm:^15.1.0" @@ -28062,19 +28062,19 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^2.0.0, nanoid@npm:^2.1.6": +"nanoid@npm:^2.0.0": version: 2.1.11 resolution: "nanoid@npm:2.1.11" checksum: 10/cf2a2eedcf9d8893a4687f11743ccf8381f047bc2b3d3887a23721bbef8fe64c5759b9cba6eb945e40efeb4a7e7379b3417e4dc5f6cc03050322d2c24a7ff69b languageName: node linkType: hard -"nanoid@npm:^3.1.31, nanoid@npm:^3.3.7": - version: 3.3.7 - resolution: "nanoid@npm:3.3.7" +"nanoid@npm:^3.1.31, nanoid@npm:^3.3.7, nanoid@npm:^3.3.8": + version: 3.3.8 + resolution: "nanoid@npm:3.3.8" bin: nanoid: bin/nanoid.cjs - checksum: 10/ac1eb60f615b272bccb0e2b9cd933720dad30bf9708424f691b8113826bb91aca7e9d14ef5d9415a6ba15c266b37817256f58d8ce980c82b0ba3185352565679 + checksum: 10/2d1766606cf0d6f47b6f0fdab91761bb81609b2e3d367027aff45e6ee7006f660fb7e7781f4a34799fe6734f1268eeed2e37a5fdee809ade0c2d4eb11b0f9c40 languageName: node linkType: hard From 506995095c53ee3d144fc0f727df73fe13ec6c8f Mon Sep 17 00:00:00 2001 From: AugmentedMode <31675118+AugmentedMode@users.noreply.github.com> Date: Fri, 20 Dec 2024 14:39:19 -0500 Subject: [PATCH 6/8] fix (cherry-pick):Add main frame URL property to req object whenever req is triggered from an iframe #29337 (#29405) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chery pick PR: #29337 into `V12.9.3` ## **Description** See the attached issue in metamask planning for more details. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/29337?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to `https://develop.d3bkcslj57l47p.amplifyapp.com/` 2. Click on Proceed anyways (This phishing warning page here is expected) 3. Open the network tab to monitor network requests 4. Connect your wallet and click on a signature or transaction 5. Verify that mainFrameOrigin is included in the payload of the network request to the security alerts API Screenshot 2024-12-20 at 10 46 05 AM ## **Screenshots/Recordings** Below are screenshots demonstrating the behavior of a test HTML page I created: 1. In the first screenshot, before the iframe is loaded, the console shows only the origin of the main frame. 2. In the second screenshot, after clicking the button to load an iframe pointing to example.com, the solution correctly identifies both the mainFrameOrigin (main frame) and the origin (iframe). Screenshot 2024-12-18 at 10 24 48 PM Screenshot 2024-12-18 at 10 24 54 PM ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.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. --- .../lib/createMainFrameOriginMiddleware.ts | 24 +++++++++++++++++++ app/scripts/metamask-controller.js | 22 ++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 app/scripts/lib/createMainFrameOriginMiddleware.ts diff --git a/app/scripts/lib/createMainFrameOriginMiddleware.ts b/app/scripts/lib/createMainFrameOriginMiddleware.ts new file mode 100644 index 000000000000..bcbc2cb7d6fd --- /dev/null +++ b/app/scripts/lib/createMainFrameOriginMiddleware.ts @@ -0,0 +1,24 @@ +// Request and responses are currently untyped. +/* eslint-disable @typescript-eslint/no-explicit-any */ + +/** + * Returns a middleware that appends the mainFrameOrigin to request + * + * @param {{ mainFrameOrigin: string }} opts - The middleware options + * @returns {Function} + */ + +export default function createMainFrameOriginMiddleware({ + mainFrameOrigin, +}: { + mainFrameOrigin: string; +}) { + return function mainFrameOriginMiddleware( + req: any, + _res: any, + next: () => void, + ) { + req.mainFrameOrigin = mainFrameOrigin; + next(); + }; +} diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 41687031aec5..3d73379dfc93 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -296,6 +296,7 @@ import { createUnsupportedMethodMiddleware, } from './lib/rpc-method-middleware'; import createOriginMiddleware from './lib/createOriginMiddleware'; +import createMainFrameOriginMiddleware from './lib/createMainFrameOriginMiddleware'; import createTabIdMiddleware from './lib/createTabIdMiddleware'; import { NetworkOrderController } from './controllers/network-order'; import { AccountOrderController } from './controllers/account-order'; @@ -5669,11 +5670,18 @@ export default class MetamaskController extends EventEmitter { tabId = sender.tab.id; } + let mainFrameOrigin = origin; + if (sender.tab && sender.tab.url) { + // If sender origin is an iframe, then get the top-level frame's origin + mainFrameOrigin = new URL(sender.tab.url).origin; + } + const engine = this.setupProviderEngineEip1193({ origin, sender, subjectType, tabId, + mainFrameOrigin, }); const dupeReqFilterStream = createDupeReqFilterStream(); @@ -5794,13 +5802,25 @@ export default class MetamaskController extends EventEmitter { * @param {MessageSender | SnapSender} options.sender - The sender object. * @param {string} options.subjectType - The type of the sender subject. * @param {tabId} [options.tabId] - The tab ID of the sender - if the sender is within a tab + * @param {mainFrameOrigin} [options.mainFrameOrigin] - The origin of the main frame if the sender is an iframe */ - setupProviderEngineEip1193({ origin, subjectType, sender, tabId }) { + setupProviderEngineEip1193({ + origin, + subjectType, + sender, + tabId, + mainFrameOrigin, + }) { const engine = new JsonRpcEngine(); // Append origin to each request engine.push(createOriginMiddleware({ origin })); + // Append mainFrameOrigin to each request if present + if (mainFrameOrigin) { + engine.push(createMainFrameOriginMiddleware({ mainFrameOrigin })); + } + // Append selectedNetworkClientId to each request engine.push(createSelectedNetworkMiddleware(this.controllerMessenger)); From 9788cef6e16072b073b5163b57fad9c75508a3fa Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Fri, 20 Dec 2024 16:57:56 -0330 Subject: [PATCH 7/8] Changelog v12.9.3 (#29407) --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65ffb876707e..869e3a70a93a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ## [12.9.3] +### Fixed +- Fix some cases where users were incorrectly seeing 0 token balances ([#29361](https://github.com/MetaMask/metamask-extension/pull/29361)) +- Ensure users that opt out of smart transaction decoding don't send network requests to related APIs ([#29341](https://github.com/ +MetaMask/metamask-extension/pull/29341)) ## [12.9.2] ### Changed From 19635b434187363920226f3541c666209daeaee7 Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Fri, 20 Dec 2024 20:39:21 +0000 Subject: [PATCH 8/8] Update Attributions --- attribution.txt | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/attribution.txt b/attribution.txt index aaeef31a2fbe..30695b63c042 100644 --- a/attribution.txt +++ b/attribution.txt @@ -21249,33 +21249,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI ****************************** nanoid -2.1.11 -The MIT License (MIT) - -Copyright 2017 Andrey Sitnik - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -****************************** - -nanoid -3.3.7 +3.3.8 The MIT License (MIT) Copyright 2017 Andrey Sitnik