@@ -21,6 +21,7 @@ import {
21
21
import { DomSanitizer , SafeResourceUrl , SafeHtml } from '@angular/platform-browser' ;
22
22
import { forkJoin , Observable , of as observableOf , throwError as observableThrow } from 'rxjs' ;
23
23
import { catchError , finalize , map , share , tap } from 'rxjs/operators' ;
24
+ import { TrustedHTML , trustedHTMLFromString } from './trusted-types' ;
24
25
25
26
26
27
/**
@@ -96,12 +97,12 @@ class SvgIconConfig {
96
97
97
98
constructor (
98
99
public url : SafeResourceUrl ,
99
- public svgText : string | null ,
100
+ public svgText : TrustedHTML | null ,
100
101
public options ?: IconOptions ) { }
101
102
}
102
103
103
104
/** Icon configuration whose content has already been loaded. */
104
- type LoadedSvgIconConfig = SvgIconConfig & { svgText : string } ;
105
+ type LoadedSvgIconConfig = SvgIconConfig & { svgText : TrustedHTML } ;
105
106
106
107
/**
107
108
* Service to register and display icons used by the `<mat-icon>` component.
@@ -129,7 +130,7 @@ export class MatIconRegistry implements OnDestroy {
129
130
private _cachedIconsByUrl = new Map < string , SVGElement > ( ) ;
130
131
131
132
/** In-progress icon fetches. Used to coalesce multiple requests to the same URL. */
132
- private _inProgressUrlFetches = new Map < string , Observable < string > > ( ) ;
133
+ private _inProgressUrlFetches = new Map < string , Observable < TrustedHTML > > ( ) ;
133
134
134
135
/** Map from font identifiers to their CSS class names. Used for icon fonts. */
135
136
private _fontCssClassesByAlias = new Map < string , string > ( ) ;
@@ -209,8 +210,10 @@ export class MatIconRegistry implements OnDestroy {
209
210
throw getMatIconFailedToSanitizeLiteralError ( literal ) ;
210
211
}
211
212
213
+ // Security: The literal is passed in as SafeHtml, and is thus trusted.
214
+ const trustedLiteral = trustedHTMLFromString ( cleanLiteral ) ;
212
215
return this . _addSvgIconConfig ( namespace , iconName ,
213
- new SvgIconConfig ( '' , cleanLiteral , options ) ) ;
216
+ new SvgIconConfig ( '' , trustedLiteral , options ) ) ;
214
217
}
215
218
216
219
/**
@@ -251,7 +254,9 @@ export class MatIconRegistry implements OnDestroy {
251
254
throw getMatIconFailedToSanitizeLiteralError ( literal ) ;
252
255
}
253
256
254
- return this . _addSvgIconSetConfig ( namespace , new SvgIconConfig ( '' , cleanLiteral , options ) ) ;
257
+ // Security: The literal is passed in as SafeHtml, and is thus trusted.
258
+ const trustedLiteral = trustedHTMLFromString ( cleanLiteral ) ;
259
+ return this . _addSvgIconSetConfig ( namespace , new SvgIconConfig ( '' , trustedLiteral , options ) ) ;
255
260
}
256
261
257
262
/**
@@ -399,7 +404,7 @@ export class MatIconRegistry implements OnDestroy {
399
404
400
405
// Not found in any cached icon sets. If there are icon sets with URLs that we haven't
401
406
// fetched, fetch them now and look for iconName in the results.
402
- const iconSetFetchRequests : Observable < string | null > [ ] = iconSetConfigs
407
+ const iconSetFetchRequests : Observable < TrustedHTML | null > [ ] = iconSetConfigs
403
408
. filter ( iconSetConfig => ! iconSetConfig . svgText )
404
409
. map ( iconSetConfig => {
405
410
return this . _loadSvgIconSetFromConfig ( iconSetConfig ) . pipe (
@@ -444,7 +449,7 @@ export class MatIconRegistry implements OnDestroy {
444
449
// the parsing by doing a quick check using `indexOf` to see if there's any chance for the
445
450
// icon to be in the set. This won't be 100% accurate, but it should help us avoid at least
446
451
// some of the parsing.
447
- if ( config . svgText && config . svgText . indexOf ( iconName ) > - 1 ) {
452
+ if ( config . svgText && config . svgText . toString ( ) . indexOf ( iconName ) > - 1 ) {
448
453
const svg = this . _svgElementFromConfig ( config as LoadedSvgIconConfig ) ;
449
454
const foundIcon = this . _extractSvgIconFromSet ( svg , iconName , config . options ) ;
450
455
if ( foundIcon ) {
@@ -470,7 +475,7 @@ export class MatIconRegistry implements OnDestroy {
470
475
* Loads the content of the icon set URL specified in the
471
476
* SvgIconConfig and attaches it to the config.
472
477
*/
473
- private _loadSvgIconSetFromConfig ( config : SvgIconConfig ) : Observable < string | null > {
478
+ private _loadSvgIconSetFromConfig ( config : SvgIconConfig ) : Observable < TrustedHTML | null > {
474
479
if ( config . svgText ) {
475
480
return observableOf ( null ) ;
476
481
}
@@ -516,7 +521,7 @@ export class MatIconRegistry implements OnDestroy {
516
521
// have to create an empty SVG node using innerHTML and append its content.
517
522
// Elements created using DOMParser.parseFromString have the same problem.
518
523
// http://stackoverflow.com/questions/23003278/svg-innerhtml-in-firefox-can-not-display
519
- const svg = this . _svgElementFromString ( '<svg></svg>' ) ;
524
+ const svg = this . _svgElementFromString ( trustedHTMLFromString ( '<svg></svg>' ) ) ;
520
525
// Clone the node so we don't remove it from the parent icon set element.
521
526
svg . appendChild ( iconElement ) ;
522
527
@@ -526,9 +531,9 @@ export class MatIconRegistry implements OnDestroy {
526
531
/**
527
532
* Creates a DOM element from the given SVG string.
528
533
*/
529
- private _svgElementFromString ( str : string ) : SVGElement {
534
+ private _svgElementFromString ( str : TrustedHTML ) : SVGElement {
530
535
const div = this . _document . createElement ( 'DIV' ) ;
531
- div . innerHTML = str ;
536
+ div . innerHTML = str as unknown as string ;
532
537
const svg = div . querySelector ( 'svg' ) as SVGElement ;
533
538
534
539
// TODO: add an ngDevMode check
@@ -543,7 +548,7 @@ export class MatIconRegistry implements OnDestroy {
543
548
* Converts an element into an SVG node by cloning all of its children.
544
549
*/
545
550
private _toSvgElement ( element : Element ) : SVGElement {
546
- const svg = this . _svgElementFromString ( '<svg></svg>' ) ;
551
+ const svg = this . _svgElementFromString ( trustedHTMLFromString ( '<svg></svg>' ) ) ;
547
552
const attributes = element . attributes ;
548
553
549
554
// Copy over all the attributes from the `symbol` to the new SVG, except the id.
@@ -585,7 +590,7 @@ export class MatIconRegistry implements OnDestroy {
585
590
* Returns an Observable which produces the string contents of the given icon. Results may be
586
591
* cached, so future calls with the same URL may not cause another HTTP request.
587
592
*/
588
- private _fetchIcon ( iconConfig : SvgIconConfig ) : Observable < string > {
593
+ private _fetchIcon ( iconConfig : SvgIconConfig ) : Observable < TrustedHTML > {
589
594
const { url : safeUrl , options} = iconConfig ;
590
595
const withCredentials = options ?. withCredentials ?? false ;
591
596
@@ -615,6 +620,11 @@ export class MatIconRegistry implements OnDestroy {
615
620
}
616
621
617
622
const req = this . _httpClient . get ( url , { responseType : 'text' , withCredentials} ) . pipe (
623
+ map ( svg => {
624
+ // Security: This SVG is fetched from a SafeResourceUrl, and is thus
625
+ // trusted HTML.
626
+ return trustedHTMLFromString ( svg ) ;
627
+ } ) ,
618
628
finalize ( ( ) => this . _inProgressUrlFetches . delete ( url ) ) ,
619
629
share ( ) ,
620
630
) ;
0 commit comments