diff --git a/packages/snaps-utils/coverage.json b/packages/snaps-utils/coverage.json index 7c789ce487..bb96afe355 100644 --- a/packages/snaps-utils/coverage.json +++ b/packages/snaps-utils/coverage.json @@ -1,6 +1,6 @@ { "branches": 99.73, "functions": 98.9, - "lines": 99.43, - "statements": 96.33 + "lines": 99.44, + "statements": 96.34 } diff --git a/packages/snaps-utils/src/ui.test.tsx b/packages/snaps-utils/src/ui.test.tsx index 569ace6a65..5532c472fa 100644 --- a/packages/snaps-utils/src/ui.test.tsx +++ b/packages/snaps-utils/src/ui.test.tsx @@ -554,6 +554,30 @@ describe('validateLink', () => { expect(fn).toHaveBeenCalledWith('bar.com'); }); + it('passes for a valid list of emails', () => { + const fn = jest.fn().mockReturnValue(false); + + expect(() => + validateLink('mailto:foo@bar.com,bar@baz.com,baz@qux.com', fn), + ).not.toThrow(); + + expect(fn).toHaveBeenCalledTimes(3); + expect(fn).toHaveBeenCalledWith('bar.com'); + expect(fn).toHaveBeenCalledWith('baz.com'); + expect(fn).toHaveBeenCalledWith('qux.com'); + }); + + it('passes for a valid email with a parameter', () => { + const fn = jest.fn().mockReturnValue(false); + + expect(() => + validateLink('mailto:foo@bar.com?subject=Subject', fn), + ).not.toThrow(); + + expect(fn).toHaveBeenCalledTimes(1); + expect(fn).toHaveBeenCalledWith('bar.com'); + }); + it('throws an error for an invalid protocol', () => { const fn = jest.fn().mockReturnValue(false); @@ -595,6 +619,44 @@ describe('validateLink', () => { expect(fn).toHaveBeenCalledTimes(1); expect(fn).toHaveBeenCalledWith('test.metamask-phishing.io'); }); + + it('throws an error for a phishing email when using multiple emails', () => { + const fn = jest.fn().mockImplementation((email) => { + if (email === 'test.metamask-phishing.io') { + return true; + } + + return false; + }); + + expect(() => + validateLink('mailto:foo@test.metamask-phishing.io,foo@bar.com', fn), + ).toThrow('Invalid URL: The specified URL is not allowed.'); + + expect(fn).toHaveBeenCalledTimes(1); + expect(fn).toHaveBeenCalledWith('test.metamask-phishing.io'); + }); + + it('throws an error for a phishing email when using parameters', () => { + const fn = jest.fn().mockImplementation((email) => { + if (email === 'test.metamask-phishing.io') { + return true; + } + + return false; + }); + + expect(() => + validateLink( + 'mailto:foo@bar.com,foo@test.metamask-phishing.io?subject=Subject', + fn, + ), + ).toThrow('Invalid URL: The specified URL is not allowed.'); + + expect(fn).toHaveBeenCalledTimes(2); + expect(fn).toHaveBeenCalledWith('bar.com'); + expect(fn).toHaveBeenCalledWith('test.metamask-phishing.io'); + }); }); describe('validateTextLinks', () => { diff --git a/packages/snaps-utils/src/ui.tsx b/packages/snaps-utils/src/ui.tsx index b53c8b8ef4..9af9380c4d 100644 --- a/packages/snaps-utils/src/ui.tsx +++ b/packages/snaps-utils/src/ui.tsx @@ -342,10 +342,23 @@ export function validateLink( `Protocol must be one of: ${ALLOWED_PROTOCOLS.join(', ')}.`, ); - const hostname = - url.protocol === 'mailto:' ? url.pathname.split('@')[1] : url.hostname; + if (url.protocol === 'mailto:') { + const emails = url.pathname.split(','); + for (const email of emails) { + const hostname = email.split('@')[1]; + assert( + !isOnPhishingList(hostname), + 'The specified URL is not allowed.', + ); + } - assert(!isOnPhishingList(hostname), 'The specified URL is not allowed.'); + return; + } + + assert( + !isOnPhishingList(url.hostname), + 'The specified URL is not allowed.', + ); } catch (error) { throw new Error( `Invalid URL: ${