@@ -21,6 +21,7 @@ import {
2121import { DomSanitizer , SafeResourceUrl , SafeHtml } from '@angular/platform-browser' ;
2222import { forkJoin , Observable , of as observableOf , throwError as observableThrow } from 'rxjs' ;
2323import { catchError , finalize , map , share , tap } from 'rxjs/operators' ;
24+ import { TrustedHTML , trustedHTMLFromString } from './trusted-types' ;
2425
2526
2627/**
@@ -96,12 +97,12 @@ class SvgIconConfig {
9697
9798 constructor (
9899 public url : SafeResourceUrl ,
99- public svgText : string | null ,
100+ public svgText : TrustedHTML | null ,
100101 public options ?: IconOptions ) { }
101102}
102103
103104/** Icon configuration whose content has already been loaded. */
104- type LoadedSvgIconConfig = SvgIconConfig & { svgText : string } ;
105+ type LoadedSvgIconConfig = SvgIconConfig & { svgText : TrustedHTML } ;
105106
106107/**
107108 * Service to register and display icons used by the `<mat-icon>` component.
@@ -129,7 +130,7 @@ export class MatIconRegistry implements OnDestroy {
129130 private _cachedIconsByUrl = new Map < string , SVGElement > ( ) ;
130131
131132 /** 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 > > ( ) ;
133134
134135 /** Map from font identifiers to their CSS class names. Used for icon fonts. */
135136 private _fontCssClassesByAlias = new Map < string , string > ( ) ;
@@ -209,8 +210,10 @@ export class MatIconRegistry implements OnDestroy {
209210 throw getMatIconFailedToSanitizeLiteralError ( literal ) ;
210211 }
211212
213+ // Security: The literal is passed in as SafeHtml, and is thus trusted.
214+ const trustedLiteral = trustedHTMLFromString ( cleanLiteral ) ;
212215 return this . _addSvgIconConfig ( namespace , iconName ,
213- new SvgIconConfig ( '' , cleanLiteral , options ) ) ;
216+ new SvgIconConfig ( '' , trustedLiteral , options ) ) ;
214217 }
215218
216219 /**
@@ -251,7 +254,9 @@ export class MatIconRegistry implements OnDestroy {
251254 throw getMatIconFailedToSanitizeLiteralError ( literal ) ;
252255 }
253256
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 ) ) ;
255260 }
256261
257262 /**
@@ -399,7 +404,7 @@ export class MatIconRegistry implements OnDestroy {
399404
400405 // Not found in any cached icon sets. If there are icon sets with URLs that we haven't
401406 // 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
403408 . filter ( iconSetConfig => ! iconSetConfig . svgText )
404409 . map ( iconSetConfig => {
405410 return this . _loadSvgIconSetFromConfig ( iconSetConfig ) . pipe (
@@ -444,7 +449,7 @@ export class MatIconRegistry implements OnDestroy {
444449 // the parsing by doing a quick check using `indexOf` to see if there's any chance for the
445450 // icon to be in the set. This won't be 100% accurate, but it should help us avoid at least
446451 // some of the parsing.
447- if ( config . svgText && config . svgText . indexOf ( iconName ) > - 1 ) {
452+ if ( config . svgText && config . svgText . toString ( ) . indexOf ( iconName ) > - 1 ) {
448453 const svg = this . _svgElementFromConfig ( config as LoadedSvgIconConfig ) ;
449454 const foundIcon = this . _extractSvgIconFromSet ( svg , iconName , config . options ) ;
450455 if ( foundIcon ) {
@@ -470,7 +475,7 @@ export class MatIconRegistry implements OnDestroy {
470475 * Loads the content of the icon set URL specified in the
471476 * SvgIconConfig and attaches it to the config.
472477 */
473- private _loadSvgIconSetFromConfig ( config : SvgIconConfig ) : Observable < string | null > {
478+ private _loadSvgIconSetFromConfig ( config : SvgIconConfig ) : Observable < TrustedHTML | null > {
474479 if ( config . svgText ) {
475480 return observableOf ( null ) ;
476481 }
@@ -516,7 +521,7 @@ export class MatIconRegistry implements OnDestroy {
516521 // have to create an empty SVG node using innerHTML and append its content.
517522 // Elements created using DOMParser.parseFromString have the same problem.
518523 // 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>' ) ) ;
520525 // Clone the node so we don't remove it from the parent icon set element.
521526 svg . appendChild ( iconElement ) ;
522527
@@ -526,9 +531,9 @@ export class MatIconRegistry implements OnDestroy {
526531 /**
527532 * Creates a DOM element from the given SVG string.
528533 */
529- private _svgElementFromString ( str : string ) : SVGElement {
534+ private _svgElementFromString ( str : TrustedHTML ) : SVGElement {
530535 const div = this . _document . createElement ( 'DIV' ) ;
531- div . innerHTML = str ;
536+ div . innerHTML = str as unknown as string ;
532537 const svg = div . querySelector ( 'svg' ) as SVGElement ;
533538
534539 // TODO: add an ngDevMode check
@@ -543,7 +548,7 @@ export class MatIconRegistry implements OnDestroy {
543548 * Converts an element into an SVG node by cloning all of its children.
544549 */
545550 private _toSvgElement ( element : Element ) : SVGElement {
546- const svg = this . _svgElementFromString ( '<svg></svg>' ) ;
551+ const svg = this . _svgElementFromString ( trustedHTMLFromString ( '<svg></svg>' ) ) ;
547552 const attributes = element . attributes ;
548553
549554 // Copy over all the attributes from the `symbol` to the new SVG, except the id.
@@ -585,7 +590,7 @@ export class MatIconRegistry implements OnDestroy {
585590 * Returns an Observable which produces the string contents of the given icon. Results may be
586591 * cached, so future calls with the same URL may not cause another HTTP request.
587592 */
588- private _fetchIcon ( iconConfig : SvgIconConfig ) : Observable < string > {
593+ private _fetchIcon ( iconConfig : SvgIconConfig ) : Observable < TrustedHTML > {
589594 const { url : safeUrl , options} = iconConfig ;
590595 const withCredentials = options ?. withCredentials ?? false ;
591596
@@ -615,6 +620,11 @@ export class MatIconRegistry implements OnDestroy {
615620 }
616621
617622 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+ } ) ,
618628 finalize ( ( ) => this . _inProgressUrlFetches . delete ( url ) ) ,
619629 share ( ) ,
620630 ) ;
0 commit comments