import { describe, it, expect } from 'vitest'; import { sanitizeUrl, sanitizeCssString, urlDomain, sanitizeTel, sanitizeEmail, } from './sanitizeUrl'; describe('sanitizeUrl', () => { it('passes through https URLs', () => { expect(sanitizeUrl('https://example.com/')).toBe('https://example.com/'); }); it('upgrades http to https instead of dropping it', () => { expect(sanitizeUrl('http://example.com/')).toBe('https://example.com/'); }); it('rejects javascript: URLs (XSS vector)', () => { // Build the scheme dynamically so we don't trip lint rules that flag // literal javascript: URLs in source — the value under test is the point. const js = 'java' + 'script:'; expect(sanitizeUrl(js + 'alert(1)')).toBeNull(); expect(sanitizeUrl(js.toUpperCase() + 'alert(1)')).toBeNull(); }); it('rejects data:, file:, and vbscript: schemes', () => { expect(sanitizeUrl('data:text/html,')).toBeNull(); expect(sanitizeUrl('file:///etc/passwd')).toBeNull(); expect(sanitizeUrl('vbscript:msgbox(1)')).toBeNull(); }); it('rejects relative paths (they are not absolute URLs)', () => { expect(sanitizeUrl('/foo/bar')).toBeNull(); expect(sanitizeUrl('foo/bar')).toBeNull(); }); it('rejects empty / nullish input', () => { expect(sanitizeUrl('')).toBeNull(); expect(sanitizeUrl(' ')).toBeNull(); expect(sanitizeUrl(undefined)).toBeNull(); expect(sanitizeUrl(null)).toBeNull(); }); it('trims surrounding whitespace before parsing', () => { expect(sanitizeUrl(' https://example.com/ ')).toBe('https://example.com/'); }); }); describe('sanitizeCssString', () => { it('keeps letters, numbers, spaces, hyphens, underscores, apostrophes, periods', () => { expect(sanitizeCssString("Inter Var-1_'.")).toBe("Inter Var-1_'."); }); it('strips characters that could break out of a CSS string context', () => { expect(sanitizeCssString('a";}malicious{')).toBe('amalicious'); expect(sanitizeCssString('url(evil)')).toBe('urlevil'); expect(sanitizeCssString('back\\slash')).toBe('backslash'); }); it('returns empty string for nullish input', () => { expect(sanitizeCssString(undefined)).toBe(''); expect(sanitizeCssString(null)).toBe(''); expect(sanitizeCssString('')).toBe(''); }); }); describe('urlDomain', () => { it('extracts the hostname and strips a leading www.', () => { expect(urlDomain('https://www.example.com/path?q=1')).toBe('example.com'); expect(urlDomain('https://sub.example.com/')).toBe('sub.example.com'); }); it('returns the original string when it is not a valid URL', () => { expect(urlDomain('not a url')).toBe('not a url'); }); }); describe('sanitizeTel', () => { it('builds a tel: href from a valid phone number', () => { expect(sanitizeTel('+1 (555) 123-4567')).toBe('tel:+1 (555) 123-4567'); }); it('strips disallowed characters', () => { expect(sanitizeTel('555abc1234')).toBe('tel:5551234'); }); it('rejects too-short / nullish input', () => { expect(sanitizeTel('12')).toBeNull(); expect(sanitizeTel(undefined)).toBeNull(); expect(sanitizeTel(null)).toBeNull(); }); }); describe('sanitizeEmail', () => { it('builds a mailto: href from a valid email', () => { expect(sanitizeEmail('rider@skate-spots.org')).toBe('mailto:rider@skate-spots.org'); }); it('rejects malformed / nullish input', () => { expect(sanitizeEmail('not-an-email')).toBeNull(); expect(sanitizeEmail('a@b')).toBeNull(); expect(sanitizeEmail(undefined)).toBeNull(); expect(sanitizeEmail(null)).toBeNull(); }); });