@@ -20,6 +20,7 @@ import { escape } from '../common/strings.js';
20
20
import { URI } from '../common/uri.js' ;
21
21
import * as DOM from './dom.js' ;
22
22
import * as domSanitize from './domSanitize.js' ;
23
+ import { convertTagToPlaintext } from './domSanitize.js' ;
23
24
import { DomEmitter } from './event.js' ;
24
25
import { FormattedTextRenderOptions } from './formattedTextRenderer.js' ;
25
26
import { StandardKeyboardEvent } from './keyboardEvent.js' ;
@@ -211,6 +212,20 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
211
212
} ) ) ;
212
213
}
213
214
215
+ // Remove/disable inputs
216
+ for ( const input of [ ...element . getElementsByTagName ( 'input' ) ] ) {
217
+ if ( input . attributes . getNamedItem ( 'type' ) ?. value === 'checkbox' ) {
218
+ input . setAttribute ( 'disabled' , '' ) ;
219
+ } else {
220
+ if ( options . sanitizerConfig ?. replaceWithPlaintext ) {
221
+ const replacement = convertTagToPlaintext ( input ) ;
222
+ input . parentElement ?. replaceChild ( replacement , input ) ;
223
+ } else {
224
+ input . remove ( ) ;
225
+ }
226
+ }
227
+ }
228
+
214
229
return {
215
230
element,
216
231
dispose : ( ) => {
@@ -412,15 +427,12 @@ function resolveWithBaseUri(baseUri: URI, href: string): string {
412
427
}
413
428
}
414
429
415
-
416
- const selfClosingTags = [ 'area' , 'base' , 'br' , 'col' , 'command' , 'embed' , 'hr' , 'img' , 'input' , 'keygen' , 'link' , 'meta' , 'param' , 'source' , 'track' , 'wbr' ] ;
417
-
418
430
function sanitizeRenderedMarkdown (
419
431
renderedMarkdown : string ,
420
432
isTrusted : boolean | MarkdownStringTrustedOptions ,
421
433
options : MarkdownSanitizerConfig = { } ,
422
434
) : TrustedHTML {
423
- const sanitizerConfig = getSanitizerOptions ( isTrusted , options ) ;
435
+ const sanitizerConfig = getDomSanitizerConfig ( isTrusted , options ) ;
424
436
return domSanitize . sanitizeHtml ( renderedMarkdown , sanitizerConfig ) ;
425
437
}
426
438
@@ -452,6 +464,7 @@ export const allowedMarkdownHtmlAttributes = [
452
464
'type' ,
453
465
'width' ,
454
466
'start' ,
467
+ 'value' ,
455
468
456
469
// Custom markdown attributes
457
470
'data-code' ,
@@ -462,7 +475,7 @@ export const allowedMarkdownHtmlAttributes = [
462
475
'class' ,
463
476
] ;
464
477
465
- function getSanitizerOptions ( isTrusted : boolean | MarkdownStringTrustedOptions , options : MarkdownSanitizerConfig ) : domSanitize . DomSanitizerConfig {
478
+ function getDomSanitizerConfig ( isTrusted : boolean | MarkdownStringTrustedOptions , options : MarkdownSanitizerConfig ) : domSanitize . DomSanitizerConfig {
466
479
const allowedLinkSchemes = [
467
480
Schemas . http ,
468
481
Schemas . https ,
@@ -507,6 +520,7 @@ function getSanitizerOptions(isTrusted: boolean | MarkdownStringTrustedOptions,
507
520
Schemas . vscodeRemoteResource ,
508
521
]
509
522
} ,
523
+ replaceWithPlaintext : options . replaceWithPlaintext ,
510
524
_do_not_use_hooks : {
511
525
uponSanitizeAttribute : ( element , e ) => {
512
526
if ( options . customAttrSanitizer ) {
@@ -545,61 +559,6 @@ function getSanitizerOptions(isTrusted: boolean | MarkdownStringTrustedOptions,
545
559
e . keepAttr = false ;
546
560
}
547
561
} ,
548
- uponSanitizeElement : ( element , e ) => {
549
- let wantsReplaceWithPlaintext = false ;
550
- if ( e . tagName === 'input' ) {
551
- if ( element . attributes . getNamedItem ( 'type' ) ?. value === 'checkbox' ) {
552
- element . setAttribute ( 'disabled' , '' ) ;
553
- } else if ( options . replaceWithPlaintext ) {
554
- wantsReplaceWithPlaintext = true ;
555
- } else {
556
- element . remove ( ) ;
557
- return ;
558
- }
559
- }
560
-
561
- if ( options . replaceWithPlaintext && ( wantsReplaceWithPlaintext || ( ! e . allowedTags [ e . tagName ] && e . tagName !== 'body' ) ) ) {
562
- if ( element . parentElement ) {
563
- let startTagText : string ;
564
- let endTagText : string | undefined ;
565
- if ( e . tagName === '#comment' ) {
566
- startTagText = `<!--${ element . textContent } -->` ;
567
- } else {
568
- const isSelfClosing = selfClosingTags . includes ( e . tagName ) ;
569
- const attrString = element . attributes . length ?
570
- ' ' + Array . from ( element . attributes )
571
- . map ( attr => `${ attr . name } ="${ attr . value } "` )
572
- . join ( ' ' )
573
- : '' ;
574
- startTagText = `<${ e . tagName } ${ attrString } >` ;
575
- if ( ! isSelfClosing ) {
576
- endTagText = `</${ e . tagName } >` ;
577
- }
578
- }
579
-
580
- const fragment = document . createDocumentFragment ( ) ;
581
- const textNode = element . parentElement . ownerDocument . createTextNode ( startTagText ) ;
582
- fragment . appendChild ( textNode ) ;
583
- const endTagTextNode = endTagText ? element . parentElement . ownerDocument . createTextNode ( endTagText ) : undefined ;
584
- while ( element . firstChild ) {
585
- fragment . appendChild ( element . firstChild ) ;
586
- }
587
-
588
- if ( endTagTextNode ) {
589
- fragment . appendChild ( endTagTextNode ) ;
590
- }
591
-
592
- if ( element . nodeType === Node . COMMENT_NODE ) {
593
- // Workaround for https://github.com/cure53/DOMPurify/issues/1005
594
- // The comment will be deleted in the next phase. However if we try to remove it now, it will cause
595
- // an exception. Instead we insert the text node before the comment.
596
- element . parentElement . insertBefore ( fragment , element ) ;
597
- } else {
598
- element . parentElement . replaceChild ( fragment , element ) ;
599
- }
600
- }
601
- }
602
- }
603
562
}
604
563
} ;
605
564
}
0 commit comments