From f1ec927140b583c5e9994279175a4750c92f50f4 Mon Sep 17 00:00:00 2001 From: metamaskbot Date: Fri, 6 Feb 2026 19:01:56 +0000 Subject: [PATCH 01/10] bump semvar version to 7.64.1 && build version to 3653 --- android/app/build.gradle | 2 +- bitrise.yml | 4 ++-- ios/MetaMask.xcodeproj/project.pbxproj | 12 ++++++------ package.json | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 9d46584e019..a1d957401bb 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -187,7 +187,7 @@ android { applicationId "io.metamask" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionName "7.64.0" + versionName "7.64.1" versionCode 3646 testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/bitrise.yml b/bitrise.yml index ed6250b7327..4bf72fc0905 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -3469,13 +3469,13 @@ app: PROJECT_LOCATION_IOS: ios - opts: is_expand: false - VERSION_NAME: 7.64.0 + VERSION_NAME: 7.64.1 - opts: is_expand: false VERSION_NUMBER: 3646 - opts: is_expand: false - FLASK_VERSION_NAME: 7.64.0 + FLASK_VERSION_NAME: 7.64.1 - opts: is_expand: false FLASK_VERSION_NUMBER: 3646 diff --git a/ios/MetaMask.xcodeproj/project.pbxproj b/ios/MetaMask.xcodeproj/project.pbxproj index 6398f785825..748b778deff 100644 --- a/ios/MetaMask.xcodeproj/project.pbxproj +++ b/ios/MetaMask.xcodeproj/project.pbxproj @@ -1319,7 +1319,7 @@ "${inherited}", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.64.0; + MARKETING_VERSION = 7.64.1; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = ( @@ -1385,7 +1385,7 @@ "${inherited}", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.64.0; + MARKETING_VERSION = 7.64.1; ONLY_ACTIVE_ARCH = NO; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = ( @@ -1454,7 +1454,7 @@ "\"$(SRCROOT)/MetaMask/System/Library/Frameworks\"", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.64.0; + MARKETING_VERSION = 7.64.1; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = ( @@ -1518,7 +1518,7 @@ "\"$(SRCROOT)/MetaMask/System/Library/Frameworks\"", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.64.0; + MARKETING_VERSION = 7.64.1; ONLY_ACTIVE_ARCH = NO; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = ( @@ -1684,7 +1684,7 @@ "\"$(SRCROOT)/MetaMask/System/Library/Frameworks\"", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.64.0; + MARKETING_VERSION = 7.64.1; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ( "$(inherited)", @@ -1751,7 +1751,7 @@ "\"$(SRCROOT)/MetaMask/System/Library/Frameworks\"", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.64.0; + MARKETING_VERSION = 7.64.1; ONLY_ACTIVE_ARCH = NO; OTHER_CFLAGS = ( "$(inherited)", diff --git a/package.json b/package.json index c13745a14af..ab99d5ea111 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "metamask", - "version": "7.64.0", + "version": "7.64.1", "private": true, "scripts": { "install:foundryup": "yarn mm-foundryup", From 204aab38e7b6be3234c6df3f8500abc16ab97762 Mon Sep 17 00:00:00 2001 From: metamaskbot Date: Fri, 6 Feb 2026 19:02:09 +0000 Subject: [PATCH 02/10] update changelog for 7.64.1 (hotfix - no test plan) --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77753d112e3..04740d30b86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [7.64.1] + ## [7.64.0] ### Added @@ -155,7 +157,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fix: Android ANR bug (#25596) - fix(analytics): cp-7.63.1 correct capitalization in Deep link event name (#25599) -- feat(perps): sdk reconnect on native socket event (#25022) (#25573) +- feat(perps): sdk reconnect on native socket event (#25022, #25573) ## [7.63.0] @@ -10352,7 +10354,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#957](https://github.com/MetaMask/metamask-mobile/pull/957): fix timeouts (#957) - [#954](https://github.com/MetaMask/metamask-mobile/pull/954): Bugfix: onboarding navigation (#954) -[Unreleased]: https://github.com/MetaMask/metamask-mobile/compare/v7.64.0...HEAD +[Unreleased]: https://github.com/MetaMask/metamask-mobile/compare/v7.64.1...HEAD +[7.64.1]: https://github.com/MetaMask/metamask-mobile/compare/v7.64.0...v7.64.1 [7.64.0]: https://github.com/MetaMask/metamask-mobile/compare/v7.63.1...v7.64.0 [7.63.1]: https://github.com/MetaMask/metamask-mobile/compare/v7.63.0...v7.63.1 [7.63.0]: https://github.com/MetaMask/metamask-mobile/compare/v7.62.2...v7.63.0 From f7ad34ee8ef66fd0355f9eb32571f0cd537c8f24 Mon Sep 17 00:00:00 2001 From: MetaMask Bot <37885440+metamaskbot@users.noreply.github.com> Date: Fri, 6 Feb 2026 17:00:26 -0500 Subject: [PATCH 03/10] chore: sync stable into release/7.64.1 (#25801) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary This PR syncs the latest changes from `stable` into `release/7.64.1`. ## Why is this needed? A release branch (`release/7.64.0`) was merged into `stable`. This PR brings those changes (hotfixes, etc.) into `release/7.64.1`. ## Action Required **Please review and resolve any merge conflicts manually.** If there are conflicts, they will appear in this PR. Resolve them to ensure the release branch has all the latest fixes from stable. --- > [!NOTE] > **Low Risk** > Documentation-only change to the changelog with no runtime or behavioral impact. > > **Overview** > Updates `CHANGELOG.md` for `7.64.0` by adding an entry noting the CardHome button color change (`#25737`). > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 13e275650156d138e9ed33963ae87fa3ac950eb2. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). Co-authored-by: João Loureiro <175489935+joaoloureirop@users.noreply.github.com> --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77753d112e3..59ed4b9e9dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Change CardHome button colors (#25737) - Added one-click trading for Perps, allowing users to deposit funds and execute trades seamlessly within the order view (#24964) - Update slippage UI, adding option for users to set a custom slippage (#25124) - Updated stablecoin lending cta to be right-aligned and not render the percentage (#25351) From 5b62f92f575f1465f6d61d73cfa449e43fac8e1b Mon Sep 17 00:00:00 2001 From: metamaskbot Date: Fri, 6 Feb 2026 22:01:52 +0000 Subject: [PATCH 04/10] [skip ci] Bump version number to 3655 --- android/app/build.gradle | 2 +- bitrise.yml | 4 ++-- ios/MetaMask.xcodeproj/project.pbxproj | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index a1d957401bb..bb039d78fe8 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -188,7 +188,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionName "7.64.1" - versionCode 3646 + versionCode 3655 testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" manifestPlaceholders.MM_BRANCH_KEY_TEST = "$System.env.MM_BRANCH_KEY_TEST" diff --git a/bitrise.yml b/bitrise.yml index 4bf72fc0905..6a7e12f0d8b 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -3472,13 +3472,13 @@ app: VERSION_NAME: 7.64.1 - opts: is_expand: false - VERSION_NUMBER: 3646 + VERSION_NUMBER: 3655 - opts: is_expand: false FLASK_VERSION_NAME: 7.64.1 - opts: is_expand: false - FLASK_VERSION_NUMBER: 3646 + FLASK_VERSION_NUMBER: 3655 - opts: is_expand: false ANDROID_APK_LINK: '' diff --git a/ios/MetaMask.xcodeproj/project.pbxproj b/ios/MetaMask.xcodeproj/project.pbxproj index 748b778deff..db9eca5d6c8 100644 --- a/ios/MetaMask.xcodeproj/project.pbxproj +++ b/ios/MetaMask.xcodeproj/project.pbxproj @@ -1281,7 +1281,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3646; + CURRENT_PROJECT_VERSION = 3655; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1350,7 +1350,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3646; + CURRENT_PROJECT_VERSION = 3655; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1416,7 +1416,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3646; + CURRENT_PROJECT_VERSION = 3655; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1483,7 +1483,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3646; + CURRENT_PROJECT_VERSION = 3655; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1646,7 +1646,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3646; + CURRENT_PROJECT_VERSION = 3655; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1716,7 +1716,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3646; + CURRENT_PROJECT_VERSION = 3655; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; From 3a9a15cc44946969bb77e1e84e229906e915df05 Mon Sep 17 00:00:00 2001 From: metamaskbot Date: Fri, 6 Feb 2026 22:46:31 +0000 Subject: [PATCH 05/10] [skip ci] Bump version number to 3656 --- android/app/build.gradle | 2 +- bitrise.yml | 4 ++-- ios/MetaMask.xcodeproj/project.pbxproj | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index bb039d78fe8..34c8019fe38 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -188,7 +188,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionName "7.64.1" - versionCode 3655 + versionCode 3656 testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" manifestPlaceholders.MM_BRANCH_KEY_TEST = "$System.env.MM_BRANCH_KEY_TEST" diff --git a/bitrise.yml b/bitrise.yml index 6a7e12f0d8b..61b3cdad2ab 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -3472,13 +3472,13 @@ app: VERSION_NAME: 7.64.1 - opts: is_expand: false - VERSION_NUMBER: 3655 + VERSION_NUMBER: 3656 - opts: is_expand: false FLASK_VERSION_NAME: 7.64.1 - opts: is_expand: false - FLASK_VERSION_NUMBER: 3655 + FLASK_VERSION_NUMBER: 3656 - opts: is_expand: false ANDROID_APK_LINK: '' diff --git a/ios/MetaMask.xcodeproj/project.pbxproj b/ios/MetaMask.xcodeproj/project.pbxproj index db9eca5d6c8..110609fac66 100644 --- a/ios/MetaMask.xcodeproj/project.pbxproj +++ b/ios/MetaMask.xcodeproj/project.pbxproj @@ -1281,7 +1281,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3655; + CURRENT_PROJECT_VERSION = 3656; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1350,7 +1350,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3655; + CURRENT_PROJECT_VERSION = 3656; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1416,7 +1416,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3655; + CURRENT_PROJECT_VERSION = 3656; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1483,7 +1483,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3655; + CURRENT_PROJECT_VERSION = 3656; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1646,7 +1646,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3655; + CURRENT_PROJECT_VERSION = 3656; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1716,7 +1716,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3655; + CURRENT_PROJECT_VERSION = 3656; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; From b11dbc49b49821f0109eeea0a02e9e2d105c653a Mon Sep 17 00:00:00 2001 From: "runway-github[bot]" <73448015+runway-github[bot]@users.noreply.github.com> Date: Mon, 9 Feb 2026 11:25:11 +0000 Subject: [PATCH 06/10] chore(runway): cherry-pick fix: check chainRanking against ALLOWED_BRIDGE_CHAIN_IDS (#25808) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - fix: check chainRanking against ALLOWED_BRIDGE_CHAIN_IDS (#25788) ## **Description** When new networks are added to the chainRanking remote feature flag in LaunchDarkly, older app versions that don't support those networks would still surface them in the UI (destination network pills, source chain checks). This creates a forward-compatibility gap where users could see unsupported networks. This change adds client-side filtering of chainRanking against ALLOWED_BRIDGE_CHAIN_IDS — the hardcoded allowlist in @metamask/bridge-controller that defines which chains this version of the client actually supports. This ensures that chains added to the remote flag in the future are silently ignored by older app versions that lack support for them. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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. --- > [!NOTE] > **Medium Risk** > Touches bridge network selection/enablement selectors, so a mistake could hide valid networks or incorrectly disable bridging, but the change is narrow and well-covered by unit tests. > > **Overview** > Adds a client-side allowlist check (`isAllowedBridgeChainId`) so `chainRanking` entries are filtered against `ALLOWED_BRIDGE_CHAIN_IDS` before being surfaced. > > `selectSourceChainRanking` now filters by *both* supported chains and user-configured networks, `selectDestChainRanking` filters to supported chains only, and `selectIsBridgeEnabledSourceFactory` now treats a source chain as enabled only if it exists in the filtered `chainRanking`. Tests are expanded to cover EVM/non-EVM unsupported chains and the new source/dest filtering behavior. > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 557c0e3094b8f190607d5e3aa8c18615f2ac8504. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). [2726418](https://github.com/MetaMask/metamask-mobile/commit/2726418c03e789fa8bc1e7a0c5841b61b80bbc45) Co-authored-by: Bryan Fullam --- app/core/redux/slices/bridge/index.test.ts | 200 ++++++++++++++++++--- app/core/redux/slices/bridge/index.ts | 39 +++- 2 files changed, 208 insertions(+), 31 deletions(-) diff --git a/app/core/redux/slices/bridge/index.test.ts b/app/core/redux/slices/bridge/index.test.ts index 5745a235206..1166148e533 100644 --- a/app/core/redux/slices/bridge/index.test.ts +++ b/app/core/redux/slices/bridge/index.test.ts @@ -17,6 +17,7 @@ import reducer, { selectIsBridgeEnabledDest, selectIsSwapsLive, selectDestChainRanking, + selectSourceChainRanking, } from '.'; import { BridgeToken, @@ -531,25 +532,28 @@ describe('bridge slice', () => { }); it('returns false when bridge is not enabled as source for the chain', () => { - const mockState = cloneDeep(mockRootState) as unknown as RootState; - // @ts-expect-error - Mock state has correct structure at runtime - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2!.chains[ - 'eip155:1' - ].isActiveSrc = false; + const mockState = cloneDeep(mockRootState); + // Remove chain from chainRanking to disable it (chainRanking presence = enabled) + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking = + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking.filter( + (chain) => chain.chainId !== 'eip155:1', + ); - const result = selectIsBridgeEnabledSource(mockState, '0x1'); + const result = selectIsBridgeEnabledSource( + mockState as unknown as RootState, + '0x1', + ); expect(result).toBe(false); }); - it('returns undefined when chain is not in bridge config', () => { + it('returns false when chain is not in bridge config', () => { const result = selectIsBridgeEnabledSource( mockRootState as unknown as RootState, '0x999' as Hex, ); - expect(result).toBeUndefined(); + expect(result).toBe(false); }); }); @@ -597,6 +601,77 @@ describe('bridge slice', () => { }); }); + describe('selectSourceChainRanking', () => { + it('returns only supported and user-configured chains', () => { + const result = selectSourceChainRanking( + mockRootState as unknown as RootState, + ); + + // Should return chains that are both in ALLOWED_BRIDGE_CHAIN_IDS + // and in the user's configured networks + expect(Array.isArray(result)).toBe(true); + expect(result.length).toBeGreaterThan(0); + + // Ethereum (0x1) is allowed and configured in the mock state + expect(result.some((chain) => chain.chainId === 'eip155:1')).toBe(true); + }); + + it('filters out unsupported EVM chains from chainRanking', () => { + const mockState = cloneDeep(mockRootState); + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking = + [ + ...mockState.engine.backgroundState.RemoteFeatureFlagController + .remoteFeatureFlags.bridgeConfigV2.chainRanking, + { chainId: 'eip155:99999', name: 'Unsupported EVM Chain' }, + ]; + + const result = selectSourceChainRanking( + mockState as unknown as RootState, + ); + + expect(result.some((chain) => chain.chainId === 'eip155:99999')).toBe( + false, + ); + }); + + it('filters out unsupported non-EVM chains from chainRanking', () => { + const mockState = cloneDeep(mockRootState); + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking = + [ + ...mockState.engine.backgroundState.RemoteFeatureFlagController + .remoteFeatureFlags.bridgeConfigV2.chainRanking, + { chainId: 'cosmos:cosmoshub-4', name: 'Unsupported Cosmos Chain' }, + ]; + + const result = selectSourceChainRanking( + mockState as unknown as RootState, + ); + + expect( + result.some((chain) => chain.chainId === 'cosmos:cosmoshub-4'), + ).toBe(false); + }); + + it('filters out chains not in user-configured networks', () => { + const result = selectSourceChainRanking( + mockRootState as unknown as RootState, + ); + + // Optimism (0xa) is in chainRanking and ALLOWED_BRIDGE_CHAIN_IDS + // AND in the mock user's configured networks + const hasOptimism = result.some((chain) => chain.chainId === 'eip155:10'); + expect(hasOptimism).toBe(true); + + // Verify no chains appear that aren't in the user's configured networks + // The user only has Ethereum (0x1) and Optimism (0xa) configured as EVM networks + result.forEach((chain) => { + if (chain.chainId.startsWith('eip155:')) { + expect(['eip155:1', 'eip155:10']).toContain(chain.chainId); + } + }); + }); + }); + describe('selectDestChainRanking', () => { it('returns chainRanking from feature flags', () => { const result = selectDestChainRanking( @@ -622,16 +697,75 @@ describe('bridge slice', () => { expect(hasEthereum).toBe(true); }); - it('returns all chains without filtering (unlike selectSourceChainRanking)', () => { + it('returns all supported chains without filtering by user-configured networks', () => { const result = selectDestChainRanking( mockRootState as unknown as RootState, ); - // selectDestChainRanking should return all chains from feature flags + // selectDestChainRanking should return all supported chains from feature flags // This is the key difference from selectSourceChainRanking which filters // by user-configured networks expect(result.length).toBeGreaterThan(0); }); + + it('filters out unsupported EVM chains not in ALLOWED_BRIDGE_CHAIN_IDS', () => { + const mockState = cloneDeep(mockRootState); + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking = + [ + ...mockState.engine.backgroundState.RemoteFeatureFlagController + .remoteFeatureFlags.bridgeConfigV2.chainRanking, + { chainId: 'eip155:99999', name: 'Unsupported Future Chain' }, + ]; + + const result = selectDestChainRanking(mockState as unknown as RootState); + + expect(result.some((chain) => chain.chainId === 'eip155:99999')).toBe( + false, + ); + expect(result.some((chain) => chain.chainId === 'eip155:1')).toBe(true); + }); + + it('filters out unsupported non-EVM chains not in ALLOWED_BRIDGE_CHAIN_IDS', () => { + const mockState = cloneDeep(mockRootState); + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking = + [ + ...mockState.engine.backgroundState.RemoteFeatureFlagController + .remoteFeatureFlags.bridgeConfigV2.chainRanking, + { chainId: 'cosmos:cosmoshub-4', name: 'Unsupported Cosmos Chain' }, + ]; + + const result = selectDestChainRanking(mockState as unknown as RootState); + + expect( + result.some((chain) => chain.chainId === 'cosmos:cosmoshub-4'), + ).toBe(false); + expect( + result.some( + (chain) => + chain.chainId === 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', + ), + ).toBe(true); + }); + }); + + describe('selectIsBridgeEnabledSource - ALLOWED_BRIDGE_CHAIN_IDS filtering', () => { + it('returns false for a chain in chainRanking but not in ALLOWED_BRIDGE_CHAIN_IDS', () => { + const mockState = cloneDeep(mockRootState); + // Add an unsupported chain to chainRanking + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking = + [ + ...mockState.engine.backgroundState.RemoteFeatureFlagController + .remoteFeatureFlags.bridgeConfigV2.chainRanking, + { chainId: 'eip155:99999', name: 'Unsupported Future Chain' }, + ]; + + const result = selectIsBridgeEnabledSource( + mockState as unknown as RootState, + '0x1869F' as Hex, // hex for 99999 + ); + + expect(result).toBe(false); + }); }); describe('selectIsSwapsLive', () => { @@ -671,19 +805,24 @@ describe('bridge slice', () => { }); it('returns false when bridge is disabled for both source and destination', () => { - const mockState = cloneDeep(mockRootState) as unknown as RootState; - // @ts-expect-error - Mock state has correct structure at runtime - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2!.chains[ - 'eip155:1' - ].isActiveSrc = false; - // @ts-expect-error - Mock state has correct structure at runtime + const mockState = cloneDeep(mockRootState); + // Remove chain from chainRanking to disable source (chainRanking presence = enabled) + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking = + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking.filter( + (chain) => chain.chainId !== 'eip155:1', + ); + // Disable destination via chains config // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2!.chains[ + ( + mockState as any + ).engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2!.chains[ 'eip155:1' ].isActiveDest = false; - const result = selectIsSwapsLive(mockState, '0x1'); + const result = selectIsSwapsLive( + mockState as unknown as RootState, + '0x1', + ); expect(result).toBe(false); }); @@ -697,13 +836,24 @@ describe('bridge slice', () => { expect(result).toBeUndefined(); }); - it('returns false when support flag is disabled', () => { - const mockState = cloneDeep(mockRootState) as unknown as RootState; - // @ts-expect-error - Mock state has correct structure at runtime + it('returns false when support flag is disabled and source is not in chainRanking', () => { + const mockState = cloneDeep(mockRootState); + // Remove chain from chainRanking to disable source + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking = + mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2.chainRanking.filter( + (chain) => chain.chainId !== 'eip155:1', + ); + // Disable destination via support flag // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - mockState.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2!.support = false; + ( + mockState as any + ).engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags.bridgeConfigV2!.support = + false; - const result = selectIsSwapsLive(mockState, '0x1'); + const result = selectIsSwapsLive( + mockState as unknown as RootState, + '0x1', + ); expect(result).toBe(false); }); diff --git a/app/core/redux/slices/bridge/index.ts b/app/core/redux/slices/bridge/index.ts index 992bedd3fcc..44b3c8eab29 100644 --- a/app/core/redux/slices/bridge/index.ts +++ b/app/core/redux/slices/bridge/index.ts @@ -279,7 +279,26 @@ export const selectBridgeFeatureFlags = createSelector( ); /** - * Selector that returns the chainRanking from feature flags filtered by user-configured networks. + * Checks whether a CAIP chain ID from chainRanking is supported by this version of the client. + * This ensures that chains added to the remote chainRanking flag in the future + * won't be surfaced by older app versions that lack support for them. + */ +const isAllowedBridgeChainId = (caipChainId: string): boolean => { + if (caipChainId.startsWith('eip155:')) { + const hexChainId = formatChainIdToHex(caipChainId); + return ALLOWED_BRIDGE_CHAIN_IDS.includes( + hexChainId as AllowedBridgeChainIds, + ); + } + return ALLOWED_BRIDGE_CHAIN_IDS.includes( + caipChainId as AllowedBridgeChainIds, + ); +}; + +/** + * Selector that returns the chainRanking from feature flags filtered by: + * 1. Chains supported by this version of the client + * 2. User-configured networks * Used by NetworkPills in SOURCE mode to show all networks the user has added. */ export const selectSourceChainRanking = createSelector( @@ -297,6 +316,11 @@ export const selectSourceChainRanking = createSelector( return chainRanking.filter((chain) => { const { chainId } = chain; + // First, ensure this chain is supported by the current client version + if (!isAllowedBridgeChainId(chainId)) { + return false; + } + // For EVM chains (eip155:*), extract the hex chain ID and check if enabled if (chainId.startsWith('eip155:')) { const hexChainId = formatChainIdToHex(chainId); @@ -310,14 +334,17 @@ export const selectSourceChainRanking = createSelector( ); /** - * Selector that returns all chains from chainRanking (all bridge-supported networks). + * Selector that returns all chains from chainRanking that are supported by this + * version of the client. * Used by NetworkPills in DEST mode to show all available destination networks. */ export const selectDestChainRanking = createSelector( selectBridgeFeatureFlags, (bridgeFeatureFlags) => { const { chainRanking } = bridgeFeatureFlags; - return chainRanking ?? []; + return (chainRanking ?? []).filter((chain) => + isAllowedBridgeChainId(chain.chainId), + ); }, ); @@ -333,9 +360,9 @@ export const selectIsBridgeEnabledSourceFactory = createSelector( (bridgeFeatureFlags) => (chainId: Hex | CaipChainId) => { const caipChainId = formatChainIdToCaip(chainId); - return ( - bridgeFeatureFlags.support && - bridgeFeatureFlags.chains[caipChainId]?.isActiveSrc + return bridgeFeatureFlags.chainRanking?.some( + (chain) => + chain.chainId === caipChainId && isAllowedBridgeChainId(chain.chainId), ); }, ); From 52a07542247822361411f26690468f42ecd3e13f Mon Sep 17 00:00:00 2001 From: metamaskbot Date: Mon, 9 Feb 2026 11:26:37 +0000 Subject: [PATCH 07/10] [skip ci] Bump version number to 3664 --- android/app/build.gradle | 2 +- bitrise.yml | 4 ++-- ios/MetaMask.xcodeproj/project.pbxproj | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 34c8019fe38..1ec54e6bf92 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -188,7 +188,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionName "7.64.1" - versionCode 3656 + versionCode 3664 testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" manifestPlaceholders.MM_BRANCH_KEY_TEST = "$System.env.MM_BRANCH_KEY_TEST" diff --git a/bitrise.yml b/bitrise.yml index 61b3cdad2ab..93ad8f93b59 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -3472,13 +3472,13 @@ app: VERSION_NAME: 7.64.1 - opts: is_expand: false - VERSION_NUMBER: 3656 + VERSION_NUMBER: 3664 - opts: is_expand: false FLASK_VERSION_NAME: 7.64.1 - opts: is_expand: false - FLASK_VERSION_NUMBER: 3656 + FLASK_VERSION_NUMBER: 3664 - opts: is_expand: false ANDROID_APK_LINK: '' diff --git a/ios/MetaMask.xcodeproj/project.pbxproj b/ios/MetaMask.xcodeproj/project.pbxproj index 110609fac66..9e7cc0d6b2c 100644 --- a/ios/MetaMask.xcodeproj/project.pbxproj +++ b/ios/MetaMask.xcodeproj/project.pbxproj @@ -1281,7 +1281,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3656; + CURRENT_PROJECT_VERSION = 3664; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1350,7 +1350,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3656; + CURRENT_PROJECT_VERSION = 3664; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1416,7 +1416,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3656; + CURRENT_PROJECT_VERSION = 3664; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1483,7 +1483,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3656; + CURRENT_PROJECT_VERSION = 3664; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1646,7 +1646,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3656; + CURRENT_PROJECT_VERSION = 3664; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1716,7 +1716,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3656; + CURRENT_PROJECT_VERSION = 3664; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; From 2205012b6b6cd970d00fde3410f68cccf1ea2021 Mon Sep 17 00:00:00 2001 From: metamaskbot Date: Mon, 9 Feb 2026 15:21:04 +0000 Subject: [PATCH 08/10] [skip ci] Bump version number to 3667 --- android/app/build.gradle | 2 +- bitrise.yml | 4 ++-- ios/MetaMask.xcodeproj/project.pbxproj | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 1ec54e6bf92..1b30a2c851a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -188,7 +188,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionName "7.64.1" - versionCode 3664 + versionCode 3667 testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" manifestPlaceholders.MM_BRANCH_KEY_TEST = "$System.env.MM_BRANCH_KEY_TEST" diff --git a/bitrise.yml b/bitrise.yml index 93ad8f93b59..49485582692 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -3472,13 +3472,13 @@ app: VERSION_NAME: 7.64.1 - opts: is_expand: false - VERSION_NUMBER: 3664 + VERSION_NUMBER: 3667 - opts: is_expand: false FLASK_VERSION_NAME: 7.64.1 - opts: is_expand: false - FLASK_VERSION_NUMBER: 3664 + FLASK_VERSION_NUMBER: 3667 - opts: is_expand: false ANDROID_APK_LINK: '' diff --git a/ios/MetaMask.xcodeproj/project.pbxproj b/ios/MetaMask.xcodeproj/project.pbxproj index 9e7cc0d6b2c..2374bf09af8 100644 --- a/ios/MetaMask.xcodeproj/project.pbxproj +++ b/ios/MetaMask.xcodeproj/project.pbxproj @@ -1281,7 +1281,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3664; + CURRENT_PROJECT_VERSION = 3667; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1350,7 +1350,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3664; + CURRENT_PROJECT_VERSION = 3667; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1416,7 +1416,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3664; + CURRENT_PROJECT_VERSION = 3667; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1483,7 +1483,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3664; + CURRENT_PROJECT_VERSION = 3667; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1646,7 +1646,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3664; + CURRENT_PROJECT_VERSION = 3667; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1716,7 +1716,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3664; + CURRENT_PROJECT_VERSION = 3667; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; From f25832525e5c0fb5676fcaa12f8db17ac3e59e87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Loureiro?= <175489935+joaoloureirop@users.noreply.github.com> Date: Mon, 9 Feb 2026 18:45:29 +0000 Subject: [PATCH 09/10] update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 276f8ba77ec..6a47a4adbf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [7.64.1] +### Fixed + + - fix: check chainRanking against ALLOWED_BRIDGE_CHAIN_IDS (#25808) + ## [7.64.0] ### Added From ca3f53526178aa0beef1b31f7d61136445c361d4 Mon Sep 17 00:00:00 2001 From: Aslau Mario-Daniel Date: Tue, 10 Feb 2026 03:56:11 +0000 Subject: [PATCH 10/10] feat: bump axios to fix audit issue (#25853) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** **Reason for change:** `yarn audit:ci` was failing due to a high-severity vulnerability in `axios` (GHSA-43fc-jf86-j433): Denial of Service via the `__proto__` key in `mergeConfig`. Affected versions are ≤1.13.4; the project was on 1.12.2. **Solution:** - Bumped axios resolutions to `^1.13.5` in root `package.json` (both resolution entries) and in `.github/scripts/package.json`. - Added `axios` to `npmPreapprovedPackages` in `.yarnrc.yml` so Yarn’s 3-day minimal age gate allows the new release. - Ran `yarn install --no-immutable` to update the lockfile to axios 1.13.5. No code changes; dependency upgrade only. `yarn audit:ci` now passes. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: N/A ## **Manual testing steps** ```gherkin Feature: Security audit and dependency usage after axios upgrade Scenario: CI audit passes after axios upgrade Given the repo has axios resolved to 1.13.5 When I run yarn audit:ci Then the command exits with code 0 and reports no audit suggestions Scenario: App and scripts still run with upgraded axios Given the branch is checked out and dependencies are installed When I run yarn install and then run any flow that uses axios (e.g. scripts or app network calls) Then no runtime errors occur and behavior is unchanged ``` ## **Screenshots/Recordings** Not applicable (dependency-only change; no UI changes). ### **Before** N/A ### **After** N/A ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/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). - [x] 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. --- > [!NOTE] > **Medium Risk** > Dependency upgrade plus bundler resolution changes could affect runtime networking behavior or Metro module resolution, especially if any code relied on Axios’ Node build. > > **Overview** > Bumps `axios` to `^1.13.5` (and updates both root `yarn.lock` and `.github/scripts/yarn.lock`) to address the reported security advisory. > > Updates `metro.config.js` resolver logic to always redirect `axios` (and `axios/dist/node/*`) imports to `axios/dist/browser/axios.cjs`, while preserving the existing E2E-only Sentry module mocking behavior under the new unified `resolveRequest` handler. > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 520829a3b2bcfae048f333abe2d7fc097131779b. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). --------- Co-authored-by: sethkfman <10342624+sethkfman@users.noreply.github.com> Co-authored-by: Mark Stacey Co-authored-by: Cal-L --- .github/scripts/package.json | 2 +- .github/scripts/yarn.lock | 170 +++++++++++++++++++++++++++++++---- metro.config.js | 54 ++++++----- package.json | 4 +- yarn.lock | 37 +++++--- 5 files changed, 215 insertions(+), 52 deletions(-) diff --git a/.github/scripts/package.json b/.github/scripts/package.json index f4a890909e0..85ec37a545b 100644 --- a/.github/scripts/package.json +++ b/.github/scripts/package.json @@ -15,7 +15,7 @@ "dependencies": { "@actions/core": "^1.10.1", "@actions/github": "^6.0.0", - "axios": "^1.7.4", + "axios": "^1.13.5", "simple-git": "^3.25.0" }, "devDependencies": { diff --git a/.github/scripts/yarn.lock b/.github/scripts/yarn.lock index 30c7858598d..6fa2709c910 100644 --- a/.github/scripts/yarn.lock +++ b/.github/scripts/yarn.lock @@ -151,7 +151,7 @@ __metadata: "@lavamoat/allow-scripts": "npm:^3.2.0" "@lavamoat/preinstall-always-fail": "npm:^2.1.0" "@types/node": "npm:^20.16.2" - axios: "npm:^1.7.4" + axios: "npm:^1.13.5" simple-git: "npm:^3.25.0" ts-node: "npm:^10.5.0" typescript: "npm:~5.4.5" @@ -487,6 +487,20 @@ __metadata: languageName: node linkType: hard +"async-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-function@npm:1.0.0" + checksum: 10/1a09379937d846f0ce7614e75071c12826945d4e417db634156bf0e4673c495989302f52186dfa9767a1d9181794554717badd193ca2bbab046ef1da741d8efd + languageName: node + linkType: hard + +"async-generator-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-generator-function@npm:1.0.0" + checksum: 10/3d49e7acbeee9e84537f4cb0e0f91893df8eba976759875ae8ee9e3d3c82f6ecdebdb347c2fad9926b92596d93cdfc78ecc988bcdf407e40433e8e8e6fe5d78e + languageName: node + linkType: hard + "asynckit@npm:^0.4.0": version: 0.4.0 resolution: "asynckit@npm:0.4.0" @@ -494,14 +508,14 @@ __metadata: languageName: node linkType: hard -"axios@npm:^1.7.4": - version: 1.7.5 - resolution: "axios@npm:1.7.5" +"axios@npm:^1.13.5": + version: 1.13.5 + resolution: "axios@npm:1.13.5" dependencies: - follow-redirects: "npm:^1.15.6" - form-data: "npm:^4.0.0" + follow-redirects: "npm:^1.15.11" + form-data: "npm:^4.0.5" proxy-from-env: "npm:^1.1.0" - checksum: 10/6cbcfe943a84089f420a900a3a3aeb54ee94dcc9c2b81b150434896357be5d1079eff0b1bbb628597371e79f896b1bc5776df04184756ba99656ff31df9a75bf + checksum: 10/db726d09902565ef9a0632893530028310e2ec2b95b727114eca1b101450b00014133dfc3871cffc87983fb922bca7e4874d7e2826d1550a377a157cdf3f05b6 languageName: node linkType: hard @@ -560,6 +574,16 @@ __metadata: languageName: node linkType: hard +"call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2": + version: 1.0.2 + resolution: "call-bind-apply-helpers@npm:1.0.2" + dependencies: + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + checksum: 10/00482c1f6aa7cfb30fb1dbeb13873edf81cfac7c29ed67a5957d60635a56b2a4a480f1016ddbdb3395cc37900d46037fb965043a51c5c789ffeab4fc535d18b5 + languageName: node + linkType: hard + "chownr@npm:^2.0.0": version: 2.0.0 resolution: "chownr@npm:2.0.0" @@ -668,6 +692,17 @@ __metadata: languageName: node linkType: hard +"dunder-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "dunder-proto@npm:1.0.1" + dependencies: + call-bind-apply-helpers: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.2.0" + checksum: 10/5add88a3d68d42d6e6130a0cac450b7c2edbe73364bbd2fc334564418569bea97c6943a8fcd70e27130bf32afc236f30982fc4905039b703f23e9e0433c29934 + languageName: node + linkType: hard + "eastasianwidth@npm:^0.2.0": version: 0.2.0 resolution: "eastasianwidth@npm:0.2.0" @@ -712,6 +747,41 @@ __metadata: languageName: node linkType: hard +"es-define-property@npm:^1.0.1": + version: 1.0.1 + resolution: "es-define-property@npm:1.0.1" + checksum: 10/f8dc9e660d90919f11084db0a893128f3592b781ce967e4fccfb8f3106cb83e400a4032c559184ec52ee1dbd4b01e7776c7cd0b3327b1961b1a4a7008920fe78 + languageName: node + linkType: hard + +"es-errors@npm:^1.3.0": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: 10/96e65d640156f91b707517e8cdc454dd7d47c32833aa3e85d79f24f9eb7ea85f39b63e36216ef0114996581969b59fe609a94e30316b08f5f4df1d44134cf8d5 + languageName: node + linkType: hard + +"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1": + version: 1.1.1 + resolution: "es-object-atoms@npm:1.1.1" + dependencies: + es-errors: "npm:^1.3.0" + checksum: 10/54fe77de288451dae51c37bfbfe3ec86732dc3778f98f3eb3bdb4bf48063b2c0b8f9c93542656986149d08aa5be3204286e2276053d19582b76753f1a2728867 + languageName: node + linkType: hard + +"es-set-tostringtag@npm:^2.1.0": + version: 2.1.0 + resolution: "es-set-tostringtag@npm:2.1.0" + dependencies: + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.6" + has-tostringtag: "npm:^1.0.2" + hasown: "npm:^2.0.2" + checksum: 10/86814bf8afbcd8966653f731415888019d4bc4aca6b6c354132a7a75bb87566751e320369654a101d23a91c87a85c79b178bcf40332839bd347aff437c4fb65f + languageName: node + linkType: hard + "escalade@npm:^3.1.1": version: 3.2.0 resolution: "escalade@npm:3.2.0" @@ -726,13 +796,13 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.15.6": - version: 1.15.6 - resolution: "follow-redirects@npm:1.15.6" +"follow-redirects@npm:^1.15.11": + version: 1.15.11 + resolution: "follow-redirects@npm:1.15.11" peerDependenciesMeta: debug: optional: true - checksum: 10/70c7612c4cab18e546e36b991bbf8009a1a41cf85354afe04b113d1117569abf760269409cb3eb842d9f7b03d62826687086b081c566ea7b1e6613cf29030bf7 + checksum: 10/07372fd74b98c78cf4d417d68d41fdaa0be4dcacafffb9e67b1e3cf090bc4771515e65020651528faab238f10f9b9c0d9707d6c1574a6c0387c5de1042cde9ba languageName: node linkType: hard @@ -746,14 +816,16 @@ __metadata: languageName: node linkType: hard -"form-data@npm:^4.0.0": - version: 4.0.0 - resolution: "form-data@npm:4.0.0" +"form-data@npm:^4.0.5": + version: 4.0.5 + resolution: "form-data@npm:4.0.5" dependencies: asynckit: "npm:^0.4.0" combined-stream: "npm:^1.0.8" + es-set-tostringtag: "npm:^2.1.0" + hasown: "npm:^2.0.2" mime-types: "npm:^2.1.12" - checksum: 10/7264aa760a8cf09482816d8300f1b6e2423de1b02bba612a136857413fdc96d7178298ced106817655facc6b89036c6e12ae31c9eb5bdc16aabf502ae8a5d805 + checksum: 10/52ecd6e927c8c4e215e68a7ad5e0f7c1031397439672fd9741654b4a94722c4182e74cc815b225dcb5be3f4180f36428f67c6dd39eaa98af0dcfdd26c00c19cd languageName: node linkType: hard @@ -782,6 +854,13 @@ __metadata: languageName: node linkType: hard +"generator-function@npm:^2.0.0": + version: 2.0.1 + resolution: "generator-function@npm:2.0.1" + checksum: 10/eb7e7eb896c5433f3d40982b2ccacdb3dd990dd3499f14040e002b5d54572476513be8a2e6f9609f6e41ab29f2c4469307611ddbfc37ff4e46b765c326663805 + languageName: node + linkType: hard + "get-caller-file@npm:^2.0.5": version: 2.0.5 resolution: "get-caller-file@npm:2.0.5" @@ -789,6 +868,37 @@ __metadata: languageName: node linkType: hard +"get-intrinsic@npm:^1.2.6": + version: 1.3.1 + resolution: "get-intrinsic@npm:1.3.1" + dependencies: + async-function: "npm:^1.0.0" + async-generator-function: "npm:^1.0.0" + call-bind-apply-helpers: "npm:^1.0.2" + es-define-property: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.1.1" + function-bind: "npm:^1.1.2" + generator-function: "npm:^2.0.0" + get-proto: "npm:^1.0.1" + gopd: "npm:^1.2.0" + has-symbols: "npm:^1.1.0" + hasown: "npm:^2.0.2" + math-intrinsics: "npm:^1.1.0" + checksum: 10/bb579dda84caa4a3a41611bdd483dade7f00f246f2a7992eb143c5861155290df3fdb48a8406efa3dfb0b434e2c8fafa4eebd469e409d0439247f85fc3fa2cc1 + languageName: node + linkType: hard + +"get-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "get-proto@npm:1.0.1" + dependencies: + dunder-proto: "npm:^1.0.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10/4fc96afdb58ced9a67558698b91433e6b037aaa6f1493af77498d7c85b141382cf223c0e5946f334fb328ee85dfe6edd06d218eaf09556f4bc4ec6005d7f5f7b + languageName: node + linkType: hard + "glob@npm:^10.2.2, glob@npm:^10.3.10": version: 10.4.5 resolution: "glob@npm:10.4.5" @@ -805,6 +915,13 @@ __metadata: languageName: node linkType: hard +"gopd@npm:^1.2.0": + version: 1.2.0 + resolution: "gopd@npm:1.2.0" + checksum: 10/94e296d69f92dc1c0768fcfeecfb3855582ab59a7c75e969d5f96ce50c3d201fd86d5a2857c22565764d5bb8a816c7b1e58f133ec318cd56274da36c5e3fb1a1 + languageName: node + linkType: hard + "graceful-fs@npm:^4.2.6": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" @@ -812,6 +929,22 @@ __metadata: languageName: node linkType: hard +"has-symbols@npm:^1.0.3, has-symbols@npm:^1.1.0": + version: 1.1.0 + resolution: "has-symbols@npm:1.1.0" + checksum: 10/959385c98696ebbca51e7534e0dc723ada325efa3475350951363cce216d27373e0259b63edb599f72eb94d6cde8577b4b2375f080b303947e560f85692834fa + languageName: node + linkType: hard + +"has-tostringtag@npm:^1.0.2": + version: 1.0.2 + resolution: "has-tostringtag@npm:1.0.2" + dependencies: + has-symbols: "npm:^1.0.3" + checksum: 10/c74c5f5ceee3c8a5b8bc37719840dc3749f5b0306d818974141dda2471a1a2ca6c8e46b9d6ac222c5345df7a901c9b6f350b1e6d62763fec877e26609a401bfe + languageName: node + linkType: hard + "hasown@npm:^2.0.2": version: 2.0.2 resolution: "hasown@npm:2.0.2" @@ -995,6 +1128,13 @@ __metadata: languageName: node linkType: hard +"math-intrinsics@npm:^1.1.0": + version: 1.1.0 + resolution: "math-intrinsics@npm:1.1.0" + checksum: 10/11df2eda46d092a6035479632e1ec865b8134bdfc4bd9e571a656f4191525404f13a283a515938c3a8de934dbfd9c09674d9da9fa831e6eb7e22b50b197d2edd + languageName: node + linkType: hard + "mime-db@npm:1.52.0": version: 1.52.0 resolution: "mime-db@npm:1.52.0" diff --git a/metro.config.js b/metro.config.js index d8f6d610150..10aa003483a 100644 --- a/metro.config.js +++ b/metro.config.js @@ -93,29 +93,39 @@ module.exports = function (baseConfig) { buffer: '@craftzdog/react-native-buffer', 'node:buffer': '@craftzdog/react-native-buffer', }, - resolveRequest: isE2E - ? (context, moduleName, platform) => { - if (moduleName === '@sentry/react-native') { - return { - type: 'sourceFile', - filePath: path.resolve( - __dirname, - 'tests/module-mocking/sentry/react-native.ts', - ), - }; - } - if (moduleName === '@sentry/core') { - return { - type: 'sourceFile', - filePath: path.resolve( - __dirname, - 'tests/module-mocking/sentry/core.ts', - ), - }; - } - return context.resolveRequest(context, moduleName, platform); + resolveRequest: (context, moduleName, platform) => { + // Use axios browser build so Node-only deps (e.g. http2) are never pulled in + if ( + moduleName === 'axios' || + moduleName.includes('axios/dist/node/') + ) { + return { + filePath: require.resolve('axios/dist/browser/axios.cjs'), + type: 'sourceFile', + }; + } + if (isE2E) { + if (moduleName === '@sentry/react-native') { + return { + type: 'sourceFile', + filePath: path.resolve( + __dirname, + 'tests/module-mocking/sentry/react-native.ts', + ), + }; } - : defaultConfig.resolver.resolveRequest, + if (moduleName === '@sentry/core') { + return { + type: 'sourceFile', + filePath: path.resolve( + __dirname, + 'tests/module-mocking/sentry/core.ts', + ), + }; + } + } + return context.resolveRequest(context, moduleName, platform); + }, }, transformer: { babelTransformerPath: require.resolve('./metro.transform.js'), diff --git a/package.json b/package.json index ab99d5ea111..e05958b48d5 100644 --- a/package.json +++ b/package.json @@ -142,7 +142,7 @@ "d3-color": "3.1.0", "color-string": "^1.9.1", "tough-cookie": "4.1.3", - "axios": "^1.12.0", + "axios": "^1.13.5", "redux-persist-filesystem-storage/react-native-blob-util": "^0.19.9", "xmldom": "npm:@xmldom/xmldom@0.7.13", "@metamask/metamask-eth-abis": "3.1.1", @@ -349,7 +349,7 @@ "@walletconnect/utils": "^2.23.0", "@xmldom/xmldom": "^0.8.10", "asyncstorage-down": "4.2.0", - "axios": "^1.8.2", + "axios": "^1.13.5", "bignumber.js": "^9.0.1", "bitcoin-address-validation": "2.2.3", "bnjs4": "npm:bn.js@^4.12.0", diff --git a/yarn.lock b/yarn.lock index 0b5ab0c732e..41623b64b7d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22021,14 +22021,14 @@ __metadata: languageName: node linkType: hard -"axios@npm:^1.12.0": - version: 1.12.2 - resolution: "axios@npm:1.12.2" +"axios@npm:^1.13.5": + version: 1.13.5 + resolution: "axios@npm:1.13.5" dependencies: - follow-redirects: "npm:^1.15.6" - form-data: "npm:^4.0.4" + follow-redirects: "npm:^1.15.11" + form-data: "npm:^4.0.5" proxy-from-env: "npm:^1.1.0" - checksum: 10/886a79770594eaad76493fecf90344b567bd956240609b5dcd09bd0afe8d3e6f1ad6d3257a93a483b6192b409d4b673d9515a34619e3e3ed1b2c0ec2a83b20ba + checksum: 10/db726d09902565ef9a0632893530028310e2ec2b95b727114eca1b101450b00014133dfc3871cffc87983fb922bca7e4874d7e2826d1550a377a157cdf3f05b6 languageName: node linkType: hard @@ -29301,13 +29301,13 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.15.6": - version: 1.15.9 - resolution: "follow-redirects@npm:1.15.9" +"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.15.11": + version: 1.15.11 + resolution: "follow-redirects@npm:1.15.11" peerDependenciesMeta: debug: optional: true - checksum: 10/e3ab42d1097e90d28b913903841e6779eb969b62a64706a3eb983e894a5db000fbd89296f45f08885a0e54cd558ef62e81be1165da9be25a6c44920da10f424c + checksum: 10/07372fd74b98c78cf4d417d68d41fdaa0be4dcacafffb9e67b1e3cf090bc4771515e65020651528faab238f10f9b9c0d9707d6c1574a6c0387c5de1042cde9ba languageName: node linkType: hard @@ -29437,7 +29437,7 @@ __metadata: languageName: node linkType: hard -"form-data@npm:4.0.4, form-data@npm:^4.0.0, form-data@npm:^4.0.4": +"form-data@npm:4.0.4": version: 4.0.4 resolution: "form-data@npm:4.0.4" dependencies: @@ -29463,6 +29463,19 @@ __metadata: languageName: node linkType: hard +"form-data@npm:^4.0.0, form-data@npm:^4.0.5": + version: 4.0.5 + resolution: "form-data@npm:4.0.5" + dependencies: + asynckit: "npm:^0.4.0" + combined-stream: "npm:^1.0.8" + es-set-tostringtag: "npm:^2.1.0" + hasown: "npm:^2.0.2" + mime-types: "npm:^2.1.12" + checksum: 10/52ecd6e927c8c4e215e68a7ad5e0f7c1031397439672fd9741654b4a94722c4182e74cc815b225dcb5be3f4180f36428f67c6dd39eaa98af0dcfdd26c00c19cd + languageName: node + linkType: hard + "format-util@npm:^1.0.3": version: 1.0.5 resolution: "format-util@npm:1.0.5" @@ -34874,7 +34887,7 @@ __metadata: appwright: "patch:appwright@patch%3Aappwright@npm%253A0.1.45%23~/.yarn/patches/appwright-npm-0.1.45-f282bc1c1b.patch%3A%3Aversion=0.1.45&hash=3beae4#~/.yarn/patches/appwright-patch-685d6e06a0.patch" assert: "npm:^1.5.0" asyncstorage-down: "npm:4.2.0" - axios: "npm:^1.8.2" + axios: "npm:^1.13.5" babel-jest: "npm:^29.7.0" babel-loader: "npm:^9.1.3" babel-plugin-inline-import: "npm:^3.0.0"