From f28f155132a39e2ad4d117782ffbbe53eb11470e Mon Sep 17 00:00:00 2001 From: Fabio Bozzo Date: Thu, 6 Nov 2025 16:43:15 +0100 Subject: [PATCH 1/2] hotfix: handle non-standard semver formats in BIP-44 banner check - Use semver.coerce() to normalize extension versions before comparison - Add try/catch to handle invalid version formats gracefully - Add test coverage for 4-part versions like 13.9.0.150 --- .../useMultichainAccountsIntroModal.test.ts | 38 ++++++++++++++++++- ui/hooks/useMultichainAccountsIntroModal.ts | 13 ++++++- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/ui/hooks/useMultichainAccountsIntroModal.test.ts b/ui/hooks/useMultichainAccountsIntroModal.test.ts index a2faf6c2577a..dbefcde910c7 100644 --- a/ui/hooks/useMultichainAccountsIntroModal.test.ts +++ b/ui/hooks/useMultichainAccountsIntroModal.test.ts @@ -1,4 +1,4 @@ -import { lt as semverLt } from 'semver'; +import { lt as semverLt, coerce as semverCoerce } from 'semver'; // Test the core logic independently of React hooks describe('BIP-44 Banner Logic', () => { @@ -16,7 +16,14 @@ describe('BIP-44 Banner Logic', () => { const isUpgradeFromLowerThanBip44Version = Boolean( lastUpdatedFromVersion && typeof lastUpdatedFromVersion === 'string' && - semverLt(lastUpdatedFromVersion, BIP44_ACCOUNTS_INTRODUCTION_VERSION), + (() => { + try { + const coercedVersion = semverCoerce(lastUpdatedFromVersion); + return coercedVersion && semverLt(coercedVersion, BIP44_ACCOUNTS_INTRODUCTION_VERSION); + } catch { + return false; + } + })(), ); return ( @@ -171,5 +178,32 @@ describe('BIP-44 Banner Logic', () => { ); expect(result).toBe(false); }); + + it('handles invalid semver version like 13.9.0.150 (4-part version)', () => { + // This tests the bug fix for extensions with non-standard version formats + const result = shouldShowBip44Banner( + true, + true, + false, + Date.now(), + '13.9.0.150', // Invalid semver - 4 parts + true, + ); + // Should gracefully handle and return false (coerced to 13.9.0 which is >= 13.5.0) + expect(result).toBe(false); + }); + + it('handles invalid semver for older version like 13.4.0.100', () => { + const result = shouldShowBip44Banner( + true, + true, + false, + Date.now(), + '13.4.0.100', // Invalid semver - 4 parts, but coerces to 13.4.0 + true, + ); + // Should coerce to 13.4.0 and show banner + expect(result).toBe(true); + }); }); }); diff --git a/ui/hooks/useMultichainAccountsIntroModal.ts b/ui/hooks/useMultichainAccountsIntroModal.ts index 0d37eb8b4c6d..2eafadcdd67c 100644 --- a/ui/hooks/useMultichainAccountsIntroModal.ts +++ b/ui/hooks/useMultichainAccountsIntroModal.ts @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react'; -import { lt as semverLt } from 'semver'; +import { lt as semverLt, coerce as semverCoerce } from 'semver'; import { useAppSelector } from '../store/store'; import { getIsMultichainAccountsState2Enabled } from '../selectors/multichain-accounts/feature-flags'; import { getLastUpdatedFromVersion } from '../selectors/selectors'; @@ -40,10 +40,19 @@ export function useMultichainAccountsIntroModal( const isMainWalletArea = location.pathname === DEFAULT_ROUTE; // Check if this is an upgrade from a version lower than BIP-44 introduction version + // Extension versions may not be valid semver therefore we coerce them first const isUpgradeFromLowerThanBip44Version = Boolean( lastUpdatedFromVersion && typeof lastUpdatedFromVersion === 'string' && - semverLt(lastUpdatedFromVersion, BIP44_ACCOUNTS_INTRODUCTION_VERSION), + (() => { + try { + const coercedVersion = semverCoerce(lastUpdatedFromVersion); + return coercedVersion && semverLt(coercedVersion, BIP44_ACCOUNTS_INTRODUCTION_VERSION); + } catch { + // If version can't be parsed, assume it's not from an old version + return false; + } + })(), ); // Show modal only for upgrades from versions < BIP-44 introduction version From 8c10bf5bbad2a9cf6962279ab5dbe38c8f216bd8 Mon Sep 17 00:00:00 2001 From: Fabio Bozzo Date: Thu, 6 Nov 2025 16:57:06 +0100 Subject: [PATCH 2/2] prettier run --- ui/hooks/useMultichainAccountsIntroModal.test.ts | 5 ++++- ui/hooks/useMultichainAccountsIntroModal.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ui/hooks/useMultichainAccountsIntroModal.test.ts b/ui/hooks/useMultichainAccountsIntroModal.test.ts index dbefcde910c7..a84a613d402f 100644 --- a/ui/hooks/useMultichainAccountsIntroModal.test.ts +++ b/ui/hooks/useMultichainAccountsIntroModal.test.ts @@ -19,7 +19,10 @@ describe('BIP-44 Banner Logic', () => { (() => { try { const coercedVersion = semverCoerce(lastUpdatedFromVersion); - return coercedVersion && semverLt(coercedVersion, BIP44_ACCOUNTS_INTRODUCTION_VERSION); + return ( + coercedVersion && + semverLt(coercedVersion, BIP44_ACCOUNTS_INTRODUCTION_VERSION) + ); } catch { return false; } diff --git a/ui/hooks/useMultichainAccountsIntroModal.ts b/ui/hooks/useMultichainAccountsIntroModal.ts index 2eafadcdd67c..20ab848cbe41 100644 --- a/ui/hooks/useMultichainAccountsIntroModal.ts +++ b/ui/hooks/useMultichainAccountsIntroModal.ts @@ -47,7 +47,10 @@ export function useMultichainAccountsIntroModal( (() => { try { const coercedVersion = semverCoerce(lastUpdatedFromVersion); - return coercedVersion && semverLt(coercedVersion, BIP44_ACCOUNTS_INTRODUCTION_VERSION); + return ( + coercedVersion && + semverLt(coercedVersion, BIP44_ACCOUNTS_INTRODUCTION_VERSION) + ); } catch { // If version can't be parsed, assume it's not from an old version return false;