@@ -313,16 +313,78 @@ function $SanitizeProvider() {
313
313
return obj ;
314
314
}
315
315
316
- var inertBodyElement = ( function ( window ) {
317
- var doc ;
318
- if ( window . document && window . document . implementation ) {
319
- doc = window . document . implementation . createHTMLDocument ( 'inert' ) ;
316
+ /**
317
+ * Create an inert document that contains the dirty HTML that needs sanitizing
318
+ * Depending upon browser support we use one of three strategies for doing this.
319
+ * Support: Safari 10.x -> XHR strategy
320
+ * Support: Firefox -> DomParser strategy
321
+ */
322
+ var getInertBodyElement /* function(html: string): HTMLBodyElement */ = ( function ( window , document ) {
323
+ var inertDocument ;
324
+ if ( document && document . implementation ) {
325
+ inertDocument = document . implementation . createHTMLDocument ( 'inert' ) ;
320
326
} else {
321
327
throw $sanitizeMinErr ( 'noinert' , 'Can\'t create an inert html document' ) ;
322
328
}
323
- var docElement = doc . documentElement || doc . getDocumentElement ( ) ;
324
- return docElement . getElementsByTagName ( 'body' ) [ 0 ] ;
325
- } ) ( window ) ;
329
+ var inertBodyElement = ( inertDocument . documentElement || inertDocument . getDocumentElement ( ) ) . querySelector ( 'body' ) ;
330
+
331
+ // Check for the Safari 10.1 bug - which allows JS to run inside the SVG G element
332
+ inertBodyElement . innerHTML = '<svg><g onload="this.parentNode.remove()"></g></svg>' ;
333
+ if ( ! inertBodyElement . querySelector ( 'svg' ) ) {
334
+ return getInertBodyElement_XHR ;
335
+ } else {
336
+ // Check for the Firefox bug - which prevents the inner img JS from being sanitized
337
+ inertBodyElement . innerHTML = '<svg><p><style><img src="</style><img src=x onerror=alert(1)//">' ;
338
+ if ( inertBodyElement . querySelector ( 'svg img' ) ) {
339
+ return getInertBodyElement_DOMParser ;
340
+ } else {
341
+ return getInertBodyElement_InertDocument ;
342
+ }
343
+ }
344
+
345
+ function getInertBodyElement_XHR ( html ) {
346
+ // We add this dummy element to ensure that the rest of the content is parsed as expected
347
+ // e.g. leading whitespace is maintained and tags like `<meta>` do not get hoisted to the `<head>` tag.
348
+ html = '<remove></remove>' + html ;
349
+ try {
350
+ html = encodeURI ( html ) ;
351
+ } catch ( e ) {
352
+ return undefined ;
353
+ }
354
+ var xhr = new window . XMLHttpRequest ( ) ;
355
+ xhr . responseType = 'document' ;
356
+ xhr . open ( 'GET' , 'data:text/html;charset=utf-8,' + html , false ) ;
357
+ xhr . send ( null ) ;
358
+ var body = xhr . response . body ;
359
+ body . firstChild . remove ( ) ;
360
+ return body ;
361
+ }
362
+
363
+ function getInertBodyElement_DOMParser ( html ) {
364
+ // We add this dummy element to ensure that the rest of the content is parsed as expected
365
+ // e.g. leading whitespace is maintained and tags like `<meta>` do not get hoisted to the `<head>` tag.
366
+ html = '<remove></remove>' + html ;
367
+ try {
368
+ var body = new window . DOMParser ( ) . parseFromString ( html , 'text/html' ) . body ;
369
+ body . firstChild . remove ( ) ;
370
+ return body ;
371
+ } catch ( e ) {
372
+ return undefined ;
373
+ }
374
+ }
375
+
376
+ function getInertBodyElement_InertDocument ( html ) {
377
+ inertBodyElement . innerHTML = html ;
378
+
379
+ // Support: IE 9-11 only
380
+ // strip custom-namespaced attributes on IE<=11
381
+ if ( document . documentMode ) {
382
+ stripCustomNsAttrs ( inertBodyElement ) ;
383
+ }
384
+
385
+ return inertBodyElement ;
386
+ }
387
+ } ) ( window , window . document ) ;
326
388
327
389
/**
328
390
* @example
@@ -342,7 +404,9 @@ function $SanitizeProvider() {
342
404
} else if ( typeof html !== 'string' ) {
343
405
html = '' + html ;
344
406
}
345
- inertBodyElement . innerHTML = html ;
407
+
408
+ var inertBodyElement = getInertBodyElement ( html ) ;
409
+ if ( ! inertBodyElement ) return '' ;
346
410
347
411
//mXSS protection
348
412
var mXSSAttempts = 5 ;
@@ -352,13 +416,9 @@ function $SanitizeProvider() {
352
416
}
353
417
mXSSAttempts -- ;
354
418
355
- // Support: IE 9-11 only
356
- // strip custom-namespaced attributes on IE<=11
357
- if ( window . document . documentMode ) {
358
- stripCustomNsAttrs ( inertBodyElement ) ;
359
- }
360
- html = inertBodyElement . innerHTML ; //trigger mXSS
361
- inertBodyElement . innerHTML = html ;
419
+ // trigger mXSS if it is going to happen by reading and writing the innerHTML
420
+ html = inertBodyElement . innerHTML ;
421
+ inertBodyElement = getInertBodyElement ( html ) ;
362
422
} while ( html !== inertBodyElement . innerHTML ) ;
363
423
364
424
var node = inertBodyElement . firstChild ;
0 commit comments