-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathurl-safety.test.ts
More file actions
152 lines (137 loc) · 4.32 KB
/
Copy pathurl-safety.test.ts
File metadata and controls
152 lines (137 loc) · 4.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import {
hasExplicitSafeScheme,
isSafeHref,
isSafeImageSrc,
sanitizeHref,
sanitizeImageSrc
} from '../src/lib/url-safety';
describe('isSafeHref', () => {
it.each([
'https://example.com',
'http://example.com/path?q=1#frag',
'mailto:foo@example.com',
'tel:+1234567890',
'sms:+1234567890',
'xmpp:user@server',
'ircs://irc.example.com/channel',
'/relative/path',
'./sibling',
'../parent',
'#anchor',
'?query=only',
'relative-no-scheme',
''
])('accepts safe href %p', (input) => {
expect(isSafeHref(input)).toBe(true);
});
it.each([
'javascript:alert(1)',
' javascript:alert(1)',
'JaVaScRiPt:alert(1)',
'javascript:void(0)',
'\tjavascript:alert(1)',
'data:text/html,<script>alert(1)</script>',
'data:image/svg+xml,<svg onload=alert(1)>',
'vbscript:msgbox(1)',
'file:///etc/passwd',
'about:blank',
'chrome://settings',
'view-source:https://example.com',
// Unknown schemes are rejected by allowlist (e.g. browsers treat
// `path:foo` as having scheme `path`, not as a relative path).
'path:with:colons/in/segment'
])('rejects unsafe href %p', (input) => {
expect(isSafeHref(input)).toBe(false);
});
it.each([null, undefined, 0, false, {}, [], 42])('rejects non-string %p', (input) => {
expect(isSafeHref(input as unknown as string)).toBe(false);
});
});
describe('isSafeImageSrc', () => {
it.each([
'https://example.com/cat.png',
'http://example.com/cat.jpg',
'/relative/cat.gif',
'',
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
'data:image/jpeg;base64,/9j/2wBDAA=='
])('accepts safe image src %p', (input) => {
expect(isSafeImageSrc(input)).toBe(true);
});
it.each([
'javascript:alert(1)',
'data:text/html,<script>alert(1)</script>',
'data:image/svg+xml,<svg onload=alert(1)>',
'data:application/javascript,alert(1)',
'vbscript:msgbox(1)',
'file:///etc/passwd',
'ftp://example.com/cat.png'
])('rejects unsafe image src %p', (input) => {
expect(isSafeImageSrc(input)).toBe(false);
});
});
describe('hasExplicitSafeScheme (autolink gate)', () => {
it.each([
'https://example.com',
'http://example.com/path?q=1',
'HTTPS://EXAMPLE.COM',
'mailto:foo@example.com',
'tel:+1234567890',
'sms:+1234567890',
'xmpp:user@server'
])('auto-links explicit safe scheme %p', (input) => {
expect(hasExplicitSafeScheme(input)).toBe(true);
});
it.each([
// Bare host-like tokens must not auto-link just because the suffix is
// a real gTLD — this is the ENG-4850 regression.
'2.xyz',
'report.zip',
'logo.png',
'example.com',
'www.example.com',
'foo@example.com',
'config.dev',
'v2.api',
// Relative / fragment / empty are never autolinked.
'/relative/path',
'./sibling',
'#anchor',
''
])('does not auto-link bare/relative token %p', (input) => {
expect(hasExplicitSafeScheme(input)).toBe(false);
});
it.each([
'javascript:alert(1)',
'data:text/html,<script>alert(1)</script>',
'ftp://example.com',
'vbscript:msgbox(1)',
'file:///etc/passwd'
])('does not auto-link unsafe scheme %p', (input) => {
expect(hasExplicitSafeScheme(input)).toBe(false);
});
it.each([null, undefined, 42, {}])('rejects non-string %p', (input) => {
expect(hasExplicitSafeScheme(input as unknown as string)).toBe(false);
});
});
describe('sanitize*', () => {
it('sanitizeHref returns the URL when safe', () => {
expect(sanitizeHref('https://example.com')).toBe('https://example.com');
});
it('sanitizeHref returns empty string when unsafe', () => {
expect(sanitizeHref('javascript:alert(1)')).toBe('');
});
it('sanitizeImageSrc returns the URL when safe', () => {
expect(sanitizeImageSrc('https://example.com/cat.png')).toBe('https://example.com/cat.png');
});
it('sanitizeImageSrc returns empty string when unsafe', () => {
expect(sanitizeImageSrc('data:image/svg+xml,<svg onload=alert(1)>')).toBe('');
});
it('sanitizeHref preserves an empty string', () => {
expect(sanitizeHref('')).toBe('');
});
it('sanitizeHref converts null/undefined to empty string', () => {
expect(sanitizeHref(null)).toBe('');
expect(sanitizeHref(undefined)).toBe('');
});
});