1 import { sanitizeURL } from './sanitize';
2 import { MAX_HOSTNAME_LENGTH, UNSUPPORTED_SCHEMES, intoCleanHostname, intoDomainImageHostname } from './utils';
4 const VALID_SCHEMES = ['http:', 'https:', 'ftp:', 'ssh:', 'telnet:', 'irc:', 'magnet:'];
6 describe('URL validation', () => {
7 describe('`isValidURL`', () => {
8 test('should try to append `https://` when scheme is missing', () => {
9 const { valid, url } = sanitizeURL('google.com');
10 expect(valid).toBe(true);
11 expect(url).toEqual('https://google.com/');
14 test('should auto-fix scheme through URL constructor', () => {
15 const { valid, url } = sanitizeURL('https:/proton.me');
16 expect(valid).toBe(true);
17 expect(url).toEqual('https://proton.me/');
20 test('should invalidate unsupported schemes', () => {
21 UNSUPPORTED_SCHEMES.forEach((scheme) => {
22 expect(sanitizeURL(`${scheme}someurl`).valid).toBe(false);
23 expect(sanitizeURL(`${scheme}/someurl`).valid).toBe(false);
24 expect(sanitizeURL(`${scheme}//someurl`).valid).toBe(false);
25 expect(sanitizeURL('someurl', scheme).valid).toBe(false);
26 expect(sanitizeURL('someurl', `${scheme}/`).valid).toBe(false);
27 expect(sanitizeURL('someurl', `${scheme}//`).valid).toBe(false);
31 test('should invalidate URLs over `MAX_HOSTNAME_LENGTH`', () => {
32 const url = `https://${'a'.repeat(MAX_HOSTNAME_LENGTH + 1)}.com`;
33 expect(sanitizeURL(url).valid).toBe(false);
36 test('should accept any valid scheme', () => {
37 VALID_SCHEMES.forEach((scheme) => {
38 expect(sanitizeURL(`${scheme}someurl`).valid).toBe(true);
39 expect(sanitizeURL(`${scheme}//someurl`).valid).toBe(true);
40 expect(sanitizeURL('someurl', scheme).valid).toBe(true);
41 expect(sanitizeURL('someurl', scheme).valid).toBe(true);
45 test('should keep `www` in URL if present', () => {
46 expect(sanitizeURL('www.proton.me').url).toEqual('https://www.proton.me/');
47 expect(sanitizeURL('https://www.proton.me').url).toEqual('https://www.proton.me/');
48 expect(sanitizeURL('https://www.proton.me/').url).toEqual('https://www.proton.me/');
49 expect(sanitizeURL('www.proton.me/login').url).toEqual('https://www.proton.me/login');
52 test('should preserve query strings and full path', () => {
53 expect(sanitizeURL('proton.me/login/?foo=bar').url).toEqual('https://proton.me/login/?foo=bar');
54 expect(sanitizeURL('proton.me/login/?foo=bar', 'wss:').url).toEqual('wss://proton.me/login/?foo=bar');
55 expect(sanitizeURL('https://proton.me/login/?foo=bar').url).toEqual('https://proton.me/login/?foo=bar');
58 test('should preserve ports', () => {
59 expect(sanitizeURL('http://localhost:3000').url).toEqual('http://localhost:3000/');
60 expect(sanitizeURL('https://localhost:3001?foo=bar').url).toEqual('https://localhost:3001/?foo=bar');
63 test('should invalidate URLs containing internal white-spaces', () => {
64 expect(sanitizeURL('https://www.proton .me').valid).toBe(false);
65 expect(sanitizeURL('proton.me /').valid).toBe(false);
66 expect(sanitizeURL('www.proton.me/foo/ ?bar=10').valid).toBe(false);
68 expect(sanitizeURL('proton.me ').valid).toBe(true);
69 expect(sanitizeURL('www.proton.me/foo/ ').valid).toBe(true);
73 describe('`intoCleanHostname', () => {
74 test('should return null for invalid URLs', () => {
75 expect(intoCleanHostname('https://www.proton .me')).toBe(null);
76 expect(intoCleanHostname('www.proton.me/foo/ ?bar=10')).toBe(null);
77 expect(intoCleanHostname('//')).toBe(null);
80 test('should remove leading `www.`', () => {
81 expect(intoCleanHostname('proton.me')).toEqual('proton.me');
82 expect(intoCleanHostname('www.proton.me')).toEqual('proton.me');
83 expect(intoCleanHostname('www.proton.me/')).toEqual('proton.me');
84 expect(intoCleanHostname('foo.www.proton.me/')).toEqual('foo.www.proton.me');
88 describe('`intoDomainImageHostname`', () => {
89 test('should remove leading `www.`', () => {
90 expect(intoDomainImageHostname('proton.me')).toEqual('proton.me');
91 expect(intoDomainImageHostname('www.proton.me')).toEqual('proton.me');
92 expect(intoDomainImageHostname('www.proton.me/')).toEqual('proton.me');
93 expect(intoDomainImageHostname('foo.www.proton.me/')).toEqual('foo.www.proton.me');
96 test('should return null for invalid URLs', () => {
97 expect(intoDomainImageHostname('https://www.proton .me')).toBe(null);
98 expect(intoDomainImageHostname('www.proton.me/foo/ ?bar=10')).toBe(null);
99 expect(intoDomainImageHostname('//')).toBe(null);
114 ])('`%s` should return null [reserved]', (hostname) => {
115 expect(intoDomainImageHostname(hostname)).toBe(null);
121 '16.172.in-addr.arpa',
122 '17.172.in-addr.arpa',
126 ])('`%s` should return null [non-ICANN]', (hostname) => {
127 expect(intoDomainImageHostname(hostname)).toBe(null);
128 expect(intoDomainImageHostname(`${hostname}:3000`)).toBe(null);
134 '16.172.in-addr.arpa',
135 '17.172.in-addr.arpa',
139 'xn--bcher-kva.example',
140 ])('`%s` should return null [non-ICANN]', (hostname) => {
141 expect(intoDomainImageHostname(hostname)).toBe(null);
142 expect(intoDomainImageHostname(`${hostname}:3000`)).toBe(null);
143 expect(intoDomainImageHostname(`sub.${hostname}`)).toBe(null);
144 expect(intoDomainImageHostname(`sub.${hostname}:3000`)).toBe(null);
152 '2001:0db8:85a3:0000:0000:8a2e:0370:7334',
153 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329',
157 ])('`%s` should return null [IP]', (hostname) => {
158 expect(intoDomainImageHostname(hostname)).toBe(null);