@@ -7,11 +7,6 @@ import { DisposableStore, IDisposable, toDisposable } from '../common/lifecycle.
7
7
import { Schemas } from '../common/network.js' ;
8
8
import dompurify from './dompurify/dompurify.js' ;
9
9
10
- const defaultSafeProtocols = [
11
- Schemas . http ,
12
- Schemas . https ,
13
- Schemas . command ,
14
- ] ;
15
10
16
11
/**
17
12
* List of safe, non-input html tags.
@@ -83,40 +78,27 @@ export const basicMarkupHtmlTags = Object.freeze([
83
78
'var' ,
84
79
'video' ,
85
80
'wbr' ,
86
-
87
- // TODO: Move these out of the default
88
- 'select' ,
89
- 'input' ,
90
81
] ) ;
91
82
92
83
export const defaultAllowedAttrs = Object . freeze ( [
93
84
'href' ,
94
85
'target' ,
95
- 'title' ,
96
- 'name' ,
97
86
'src' ,
98
87
'alt' ,
88
+ 'title' ,
89
+ 'for' ,
90
+ 'name' ,
99
91
'role' ,
100
92
'tabindex' ,
101
- 'width' ,
102
- 'height' ,
103
- 'align' ,
104
93
'x-dispatch' ,
105
94
'required' ,
106
95
'checked' ,
107
96
'placeholder' ,
108
97
'type' ,
109
98
'start' ,
110
-
111
- // TODO: See if we can move these out of the default
112
- 'for' ,
113
- 'role' ,
114
- 'data-href' ,
115
- 'data-command' ,
116
- 'data-code' ,
117
- 'id' ,
118
- 'class' ,
119
- 'style' ,
99
+ 'width' ,
100
+ 'height' ,
101
+ 'align' ,
120
102
] ) ;
121
103
122
104
@@ -134,28 +116,35 @@ function addDompurifyHook(hook: 'uponSanitizeElement' | 'uponSanitizeAttribute',
134
116
* Hooks dompurify using `afterSanitizeAttributes` to check that all `href` and `src`
135
117
* attributes are valid.
136
118
*/
137
- function hookDomPurifyHrefAndSrcSanitizer ( allowedProtocols : readonly string [ ] | '*' , allowDataImages = false ) : IDisposable {
119
+ function hookDomPurifyHrefAndSrcSanitizer ( allowedLinkProtocols : readonly string [ ] | '*' , allowedMediaProtocols : readonly string [ ] ) : IDisposable {
138
120
// https://github.com/cure53/DOMPurify/blob/main/demos/hooks-scheme-allowlist.html
139
121
// build an anchor to map URLs to
140
122
const anchor = document . createElement ( 'a' ) ;
141
123
124
+ function validateLink ( value : string , allowedProtocols : readonly string [ ] | '*' ) : boolean {
125
+ if ( allowedProtocols === '*' ) {
126
+ return true ; // allow all protocols
127
+ }
128
+
129
+ anchor . href = value ;
130
+ return allowedProtocols . includes ( anchor . protocol . replace ( / : $ / , '' ) ) ;
131
+ }
132
+
142
133
dompurify . addHook ( 'afterSanitizeAttributes' , ( node ) => {
143
134
// check all href/src attributes for validity
144
135
for ( const attr of [ 'href' , 'src' ] ) {
145
136
if ( node . hasAttribute ( attr ) ) {
146
137
const attrValue = node . getAttribute ( attr ) as string ;
147
- if ( attr === 'href' && attrValue . startsWith ( '#' ) ) {
148
- // Allow fragment links
149
- continue ;
150
- }
138
+ if ( attr === 'href' ) {
151
139
152
- anchor . href = attrValue ;
153
- if ( allowedProtocols !== '*' && ! allowedProtocols . includes ( anchor . protocol . replace ( / : $ / , '' ) ) ) {
154
- if ( allowDataImages && attr === 'src' && anchor . href . startsWith ( 'data:' ) ) {
155
- continue ;
140
+ if ( ! attrValue . startsWith ( '#' ) && ! validateLink ( attrValue , allowedLinkProtocols ) ) {
141
+ node . removeAttribute ( attr ) ;
156
142
}
157
143
158
- node . removeAttribute ( attr ) ;
144
+ } else { // 'src'
145
+ if ( ! validateLink ( attrValue , allowedMediaProtocols ) ) {
146
+ node . removeAttribute ( attr ) ;
147
+ }
159
148
}
160
149
}
161
150
}
@@ -165,16 +154,35 @@ function hookDomPurifyHrefAndSrcSanitizer(allowedProtocols: readonly string[] |
165
154
}
166
155
167
156
export interface SanitizeOptions {
168
- readonly overrideAllowedTags ?: readonly string [ ] ;
169
- readonly overrideAllowedAttributes ?: readonly string [ ] ;
157
+ /**
158
+ * Configured the allowed html tags.
159
+ */
160
+ readonly allowedTags ?: {
161
+ readonly override ?: readonly string [ ] ;
162
+ readonly augment ?: readonly string [ ] ;
163
+ } ;
164
+
165
+ /**
166
+ * Configured the allowed html attributes.
167
+ */
168
+ readonly allowedAttributes ?: {
169
+ readonly override ?: readonly string [ ] ;
170
+ readonly augment ?: readonly string [ ] ;
171
+ } ;
170
172
171
173
/**
172
- * List of allowed protocols for `href` and `src` attributes.
173
- *
174
- * If this is
174
+ * List of allowed protocols for `href` attributes.
175
175
*/
176
- readonly overrideAllowedProtocols ?: readonly string [ ] | '*' ;
177
- readonly allowDataImages ?: boolean ;
176
+ readonly allowedLinkProtocols ?: {
177
+ readonly override ?: readonly string [ ] | '*' ;
178
+ } ;
179
+
180
+ /**
181
+ * List of allowed protocols for `src` attributes.
182
+ */
183
+ readonly allowedMediaProtocols ?: {
184
+ readonly override ?: readonly string [ ] ;
185
+ } ;
178
186
179
187
readonly hooks ?: {
180
188
readonly uponSanitizeElement ?: UponSanitizeElementCb ;
@@ -205,16 +213,29 @@ export function sanitizeHtml(untrusted: string, config?: SanitizeOptions): Trust
205
213
try {
206
214
const resolvedConfig : dompurify . Config = { ...defaultDomPurifyConfig } ;
207
215
208
- if ( config ?. overrideAllowedTags ) {
209
- resolvedConfig . ALLOWED_TAGS = [ ...config . overrideAllowedTags ] ;
216
+ if ( config ?. allowedTags ) {
217
+ if ( config . allowedTags . override ) {
218
+ resolvedConfig . ALLOWED_TAGS = [ ...config . allowedTags . override ] ;
219
+ }
220
+
221
+ if ( config ?. allowedTags ?. augment ) {
222
+ resolvedConfig . ALLOWED_TAGS = [ ...( resolvedConfig . ALLOWED_TAGS ?? [ ] ) , ...config . allowedTags . augment ] ;
223
+ }
210
224
}
211
225
212
- if ( config ?. overrideAllowedAttributes ) {
213
- resolvedConfig . ALLOWED_ATTR = [ ...config . overrideAllowedAttributes ] ;
226
+ if ( config ?. allowedAttributes ) {
227
+ if ( config . allowedAttributes . override ) {
228
+ resolvedConfig . ALLOWED_ATTR = [ ...config . allowedAttributes . override ] ;
229
+ }
230
+
231
+ if ( config ?. allowedAttributes ?. augment ) {
232
+ resolvedConfig . ALLOWED_ATTR = [ ...( resolvedConfig . ALLOWED_ATTR ?? [ ] ) , ...config . allowedAttributes . augment ] ;
233
+ }
214
234
}
215
235
216
- const allowedProtocols = config ?. overrideAllowedProtocols ?? defaultSafeProtocols ;
217
- store . add ( hookDomPurifyHrefAndSrcSanitizer ( allowedProtocols , config ?. allowDataImages ) ) ;
236
+ store . add ( hookDomPurifyHrefAndSrcSanitizer (
237
+ config ?. allowedLinkProtocols ?. override ?? [ Schemas . http , Schemas . https ] ,
238
+ config ?. allowedMediaProtocols ?. override ?? [ Schemas . http , Schemas . https ] ) ) ;
218
239
219
240
if ( config ?. hooks ?. uponSanitizeElement ) {
220
241
store . add ( addDompurifyHook ( 'uponSanitizeElement' , config ?. hooks . uponSanitizeElement ) ) ;
0 commit comments