diff --git a/packages/snap/src/ui/confirmation/utils.test.ts b/packages/snap/src/ui/confirmation/utils.test.ts index ed0e0e98..050b9791 100644 --- a/packages/snap/src/ui/confirmation/utils.test.ts +++ b/packages/snap/src/ui/confirmation/utils.test.ts @@ -6,6 +6,7 @@ import { FetchStatus } from './api'; import { ConfirmationBanner, isFetchInProgress, + formatOrigin, isLocalTransactionValidationFailed, isRemoteTransactionScanLoading, requiresMaliciousAcknowledgement, @@ -40,6 +41,73 @@ describe('confirmation utils', () => { ); }); + describe('formatOrigin', () => { + it.each([ + { + testcase: '"Unknown" for an undefined origin', + input: undefined, + expected: 'Unknown', + }, + { + testcase: '"Unknown" for an empty origin', + input: '', + expected: 'Unknown', + }, + { + testcase: '"MetaMask" for the internal metamask origin', + input: 'metamask', + expected: 'MetaMask', + }, + { + testcase: '"WalletConnect" for the wallet-connect origin', + input: 'wallet-connect', + expected: 'WalletConnect', + }, + { + testcase: 'known origins case-insensitively for metamask', + input: 'MetaMask', + expected: 'MetaMask', + }, + { + testcase: 'known origins case-insensitively for wallet-connect', + input: 'Wallet-Connect', + expected: 'WalletConnect', + }, + { + testcase: 'the hostname for an https URL', + input: 'https://example.com', + expected: 'example.com', + }, + { + testcase: 'the hostname for an https URL with path and query', + input: 'https://app.example.com/path?q=1', + expected: 'app.example.com', + }, + { + testcase: 'the hostname for an http URL', + input: 'http://example.com', + expected: 'example.com', + }, + { + testcase: 'an empty string for a non-URL string', + input: '1234abcd-channel-id', + expected: '', + }, + { + testcase: 'an empty string for a non-http URL', + input: 'ftp://example.com', + expected: '', + }, + { + testcase: 'an empty string for an invalid value', + input: 'not a url', + expected: '', + }, + ])('returns $expected for $testcase', ({ input, expected }) => { + expect(formatOrigin(input)).toStrictEqual(expected); + }); + }); + describe('isRemoteTransactionScanLoading', () => { it('disables confirm while scan is fetching', () => { expect( diff --git a/packages/snap/src/ui/confirmation/utils.ts b/packages/snap/src/ui/confirmation/utils.ts index a29bea08..66d12ff3 100644 --- a/packages/snap/src/ui/confirmation/utils.ts +++ b/packages/snap/src/ui/confirmation/utils.ts @@ -36,32 +36,54 @@ export function getNetworkName(scope: KnownCaip2ChainId): string { return NetworkName[scope] ?? 'Unknown'; } +/** + * Display labels for known, non-URL origins, keyed by their lowercased raw value. + * Lets us show a friendly name for the internal MetaMask origin and WalletConnect + * channels instead of an empty/hidden origin row. + */ +const KNOWN_ORIGIN_LABELS: Record = { + metamask: 'MetaMask', + 'wallet-connect': 'WalletConnect', +}; + /** * Formats an origin for display purposes. * * @param origin - The origin string to format (e.g., 'metamask', 'https://example.com'). - * @returns The formatted origin string (e.g., 'MetaMask', 'example.com'). + * @returns The formatted origin string. `'Unknown'` for undefined/empty origins, a + * friendly label for known origins (e.g. `'MetaMask'`, `'WalletConnect'`), the + * hostname for http(s) URLs, or an empty string for any other value (e.g. a + * WalletConnect channelId or a non-http URL) so the UI hides the origin row. */ export function formatOrigin(origin: string | undefined): string { if (!origin) { return 'Unknown'; } - // Special case: format 'metamask' as 'MetaMask' (case-insensitive) - if (origin.toLowerCase() === 'metamask') { - return 'MetaMask'; + const knownLabel = KNOWN_ORIGIN_LABELS[origin.toLowerCase()]; + if (knownLabel) { + return knownLabel; } // Try to extract hostname from URL try { - return new URL(origin).hostname; + const url = new URL(origin); + return isHttpOrHttpsUrl(url) ? url.hostname : ''; } catch { - // If not a valid URL, return the original value - // This shouldn't happen if validation is working correctly - return origin; + return ''; } } +/** + * Checks whether a parsed URL uses an HTTP(S) protocol. + * + * @param url - The parsed URL to check. + * @returns Whether the URL uses HTTP or HTTPS. + */ +function isHttpOrHttpsUrl(url: URL): boolean { + return url.protocol === 'http:' || url.protocol === 'https:'; +} + /** * Gets the locale from the preferences. *