From b534da4a82e71ade83cad075f7ca3ab5b890a72e Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Mon, 9 Sep 2024 10:21:18 +0200 Subject: [PATCH] Improve `validateLink` error handling --- packages/snaps-utils/src/ui.test.tsx | 56 ++++++++++++++++++++++++++++ packages/snaps-utils/src/ui.tsx | 5 +-- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/packages/snaps-utils/src/ui.test.tsx b/packages/snaps-utils/src/ui.test.tsx index afb16a96d3..569ace6a65 100644 --- a/packages/snaps-utils/src/ui.test.tsx +++ b/packages/snaps-utils/src/ui.test.tsx @@ -42,6 +42,7 @@ import { walkJsx, getJsxChildren, serialiseJsx, + validateLink, } from './ui'; describe('getTextChildren', () => { @@ -541,6 +542,61 @@ describe('getJsxElementFromComponent', () => { }); }); +describe('validateLink', () => { + it('passes for a valid link', () => { + const fn = jest.fn().mockReturnValue(false); + + expect(() => validateLink('https://foo.bar', fn)).not.toThrow(); + expect(() => validateLink('mailto:foo@bar.com', fn)).not.toThrow(); + + expect(fn).toHaveBeenCalledTimes(2); + expect(fn).toHaveBeenCalledWith('foo.bar'); + expect(fn).toHaveBeenCalledWith('bar.com'); + }); + + it('throws an error for an invalid protocol', () => { + const fn = jest.fn().mockReturnValue(false); + + expect(() => validateLink('http://foo.bar', fn)).toThrow( + 'Invalid URL: Protocol must be one of: https:, mailto:.', + ); + + expect(fn).not.toHaveBeenCalled(); + }); + + it('throws an error for an invalid URL', () => { + const fn = jest.fn().mockReturnValue(false); + + expect(() => validateLink('foo.bar', fn)).toThrow( + 'Invalid URL: Unable to parse URL.', + ); + + expect(fn).not.toHaveBeenCalled(); + }); + + it('throws an error for a phishing link', () => { + const fn = jest.fn().mockReturnValue(true); + + expect(() => validateLink('https://test.metamask-phishing.io', 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', () => { + const fn = jest.fn().mockReturnValue(true); + + expect(() => + validateLink('mailto:foo@test.metamask-phishing.io', fn), + ).toThrow('Invalid URL: The specified URL is not allowed.'); + + expect(fn).toHaveBeenCalledTimes(1); + expect(fn).toHaveBeenCalledWith('test.metamask-phishing.io'); + }); +}); + describe('validateTextLinks', () => { it('passes for valid links', () => { expect(() => diff --git a/packages/snaps-utils/src/ui.tsx b/packages/snaps-utils/src/ui.tsx index 9da9518ed5..b53c8b8ef4 100644 --- a/packages/snaps-utils/src/ui.tsx +++ b/packages/snaps-utils/src/ui.tsx @@ -33,7 +33,6 @@ import { import { assert, assertExhaustive, - AssertionError, hasProperty, isPlainObject, } from '@metamask/utils'; @@ -332,7 +331,7 @@ function getMarkdownLinks(text: string) { * @param isOnPhishingList - The function that checks the link against the * phishing list. */ -function validateLink( +export function validateLink( link: string, isOnPhishingList: (url: string) => boolean, ) { @@ -350,7 +349,7 @@ function validateLink( } catch (error) { throw new Error( `Invalid URL: ${ - error instanceof AssertionError ? error.message : 'Unable to parse URL.' + error?.code === 'ERR_ASSERTION' ? error.message : 'Unable to parse URL.' }`, ); }