Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/test/browser/domSanitize.test.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as assert from 'assert';
7
import { sanitizeHtml } from '../../browser/domSanitize.js';
8
import { Schemas } from '../../common/network.js';
9
import { ensureNoDisposablesAreLeakedInTestSuite } from '../common/utils.js';
10
11
suite('DomSanitize', () => {
12
13
ensureNoDisposablesAreLeakedInTestSuite();
14
15
test('removes unsupported tags by default', () => {
16
const html = '<div>safe<script>alert(1)</script>content</div>';
17
const result = sanitizeHtml(html);
18
const str = result.toString();
19
20
assert.ok(str.includes('<div>'));
21
assert.ok(str.includes('safe'));
22
assert.ok(str.includes('content'));
23
assert.ok(!str.includes('<script>'));
24
assert.ok(!str.includes('alert(1)'));
25
});
26
27
test('removes unsupported attributes by default', () => {
28
const html = '<div onclick="alert(1)" title="safe">content</div>';
29
const result = sanitizeHtml(html);
30
const str = result.toString();
31
32
assert.ok(str.includes('<div title="safe">'));
33
assert.ok(!str.includes('onclick'));
34
assert.ok(!str.includes('alert(1)'));
35
});
36
37
test('allows custom tags via config', () => {
38
{
39
const html = '<div>removed</div><custom-tag>hello</custom-tag>';
40
const result = sanitizeHtml(html, {
41
allowedTags: { override: ['custom-tag'] }
42
});
43
assert.strictEqual(result.toString(), 'removed<custom-tag>hello</custom-tag>');
44
}
45
{
46
const html = '<div>kept</div><augmented-tag>world</augmented-tag>';
47
const result = sanitizeHtml(html, {
48
allowedTags: { augment: ['augmented-tag'] }
49
});
50
assert.strictEqual(result.toString(), '<div>kept</div><augmented-tag>world</augmented-tag>');
51
}
52
});
53
54
test('allows custom attributes via config', () => {
55
const html = '<div custom-attr="value">content</div>';
56
const result = sanitizeHtml(html, {
57
allowedAttributes: { override: ['custom-attr'] }
58
});
59
const str = result.toString();
60
61
assert.ok(str.includes('custom-attr="value"'));
62
});
63
64
test('Attributes in config should be case insensitive', () => {
65
const html = '<div Custom-Attr="value">content</div>';
66
67
{
68
const result = sanitizeHtml(html, {
69
allowedAttributes: { override: ['custom-attr'] }
70
});
71
assert.ok(result.toString().includes('custom-attr="value"'));
72
}
73
{
74
const result = sanitizeHtml(html, {
75
allowedAttributes: { override: ['CUSTOM-ATTR'] }
76
});
77
assert.ok(result.toString().includes('custom-attr="value"'));
78
}
79
});
80
81
test('removes unsupported protocols for href by default', () => {
82
const html = '<a href="javascript:alert(1)">bad link</a>';
83
const result = sanitizeHtml(html);
84
const str = result.toString();
85
86
assert.ok(str.includes('<a>bad link</a>'));
87
assert.ok(!str.includes('javascript:'));
88
});
89
90
test('removes unsupported protocols for src by default', () => {
91
const html = '<img alt="text" src="javascript:alert(1)">';
92
const result = sanitizeHtml(html);
93
const str = result.toString();
94
95
assert.ok(str.includes('<img alt="text">'));
96
assert.ok(!str.includes('javascript:'));
97
});
98
99
test('allows safe protocols for href', () => {
100
const html = '<a href="https://example.com">safe link</a>';
101
const result = sanitizeHtml(html);
102
const str = result.toString();
103
104
assert.ok(str.includes('href="https://example.com"'));
105
});
106
107
test('allows fragment links', () => {
108
const html = '<a href="#section">fragment link</a>';
109
const result = sanitizeHtml(html);
110
const str = result.toString();
111
112
assert.ok(str.includes('href="#section"'));
113
});
114
115
test('removes data images by default', () => {
116
const html = '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==">';
117
const result = sanitizeHtml(html);
118
const str = result.toString();
119
120
assert.ok(str.includes('<img>'));
121
assert.ok(!str.includes('src="data:'));
122
});
123
124
test('allows data images when enabled', () => {
125
const html = '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==">';
126
const result = sanitizeHtml(html, {
127
allowedMediaProtocols: { override: [Schemas.data] }
128
});
129
const str = result.toString();
130
131
assert.ok(str.includes('src="data:image/png;base64,'));
132
});
133
134
test('Supports dynamic attribute sanitization', () => {
135
const html = '<div title="a" other="1">text1</div><div title="b" other="2">text2</div>';
136
const result = sanitizeHtml(html, {
137
allowedAttributes: {
138
override: [
139
{
140
attributeName: 'title',
141
shouldKeep: (_el, data) => {
142
return data.attrValue.includes('b');
143
}
144
}
145
]
146
}
147
});
148
const str = result.toString();
149
assert.strictEqual(str, '<div>text1</div><div title="b">text2</div>');
150
});
151
152
test('Supports changing attributes in dynamic sanitization', () => {
153
const html = '<div title="abc" other="1">text1</div><div title="xyz" other="2">text2</div>';
154
const result = sanitizeHtml(html, {
155
allowedAttributes: {
156
override: [
157
{
158
attributeName: 'title',
159
shouldKeep: (_el, data) => {
160
if (data.attrValue === 'abc') {
161
return false;
162
}
163
return data.attrValue + data.attrValue;
164
}
165
}
166
]
167
}
168
});
169
// xyz title should be preserved and doubled
170
assert.strictEqual(result.toString(), '<div>text1</div><div title="xyzxyz">text2</div>');
171
});
172
173
test('Attr name should clear previously set dynamic sanitizer', () => {
174
const html = '<div title="abc" other="1">text1</div><div title="xyz" other="2">text2</div>';
175
const result = sanitizeHtml(html, {
176
allowedAttributes: {
177
override: [
178
{
179
attributeName: 'title',
180
shouldKeep: () => false
181
},
182
'title' // Should allow everything since it comes after custom rule
183
]
184
}
185
});
186
assert.strictEqual(result.toString(), '<div title="abc">text1</div><div title="xyz">text2</div>');
187
});
188
189
suite('replaceWithPlaintext', () => {
190
191
test('replaces unsupported tags with plaintext representation', () => {
192
const html = '<div>safe<script>alert(1)</script>content</div>';
193
const result = sanitizeHtml(html, {
194
replaceWithPlaintext: true
195
});
196
const str = result.toString();
197
assert.strictEqual(str, `<div>safe&lt;script&gt;alert(1)&lt;/script&gt;content</div>`);
198
});
199
200
test('handles self-closing tags correctly', () => {
201
const html = '<div><input type="text"><custom-input /></div>';
202
const result = sanitizeHtml(html, {
203
replaceWithPlaintext: true
204
});
205
assert.strictEqual(result.toString(), '<div>&lt;input type="text"&gt;&lt;custom-input&gt;&lt;/custom-input&gt;</div>');
206
});
207
208
test('handles tags with attributes', () => {
209
const html = '<div><unknown-tag class="test" id="myid">content</unknown-tag></div>';
210
const result = sanitizeHtml(html, {
211
replaceWithPlaintext: true
212
});
213
assert.strictEqual(result.toString(), '<div>&lt;unknown-tag class="test" id="myid"&gt;content&lt;/unknown-tag&gt;</div>');
214
});
215
216
test('handles nested unsupported tags', () => {
217
const html = '<div><outer><inner>nested</inner></outer></div>';
218
const result = sanitizeHtml(html, {
219
replaceWithPlaintext: true
220
});
221
assert.strictEqual(result.toString(), '<div>&lt;outer&gt;&lt;inner&gt;nested&lt;/inner&gt;&lt;/outer&gt;</div>');
222
});
223
224
test('handles comments correctly', () => {
225
const html = '<div><!-- this is a comment -->content</div>';
226
const result = sanitizeHtml(html, {
227
replaceWithPlaintext: true
228
});
229
assert.strictEqual(result.toString(), '<div>&lt;!-- this is a comment --&gt;content</div>');
230
});
231
232
test('handles empty tags', () => {
233
const html = '<div><empty></empty></div>';
234
const result = sanitizeHtml(html, {
235
replaceWithPlaintext: true
236
});
237
assert.strictEqual(result.toString(), '<div>&lt;empty&gt;&lt;/empty&gt;</div>');
238
});
239
240
test('works with custom allowed tags configuration', () => {
241
const html = '<div><custom>allowed</custom><forbidden>not allowed</forbidden></div>';
242
const result = sanitizeHtml(html, {
243
replaceWithPlaintext: true,
244
allowedTags: { augment: ['custom'] }
245
});
246
assert.strictEqual(result.toString(), '<div><custom>allowed</custom>&lt;forbidden&gt;not allowed&lt;/forbidden&gt;</div>');
247
});
248
});
249
});
250
251