@@ -107,44 +107,56 @@ export class Xss {
107107 // used whenever untrusted remote content (eg html email) is rendered, but we still want to preserve html
108108 DOMPurify . removeAllHooks ( ) ;
109109 DOMPurify . addHook ( 'afterSanitizeAttributes' , node => {
110- if ( ! node ) {
110+ // Ensure the node is an Element
111+ if ( ! ( node instanceof Element ) ) {
111112 return ;
112113 }
113- if ( 'style' in node ) {
114+
115+ // Handle style attributes
116+ if ( node . hasAttribute ( 'style' ) ) {
114117 // mitigation rather than a fix, which will involve updating CSP, see https://github.com/FlowCrypt/flowcrypt-browser/issues/2648
115- const style = ( node as Element ) . getAttribute ( 'style' ) ?. toLowerCase ( ) ;
118+ const style = node . getAttribute ( 'style' ) ?. toLowerCase ( ) ;
116119 if ( style && ( style . includes ( 'url(' ) || style . includes ( '@import' ) ) ) {
117- ( node as Element ) . removeAttribute ( 'style' ) ; // don't want any leaks through css url()
120+ node . removeAttribute ( 'style' ) ; // don't want any leaks through css url()
118121 }
119122 // strip css styles that could use to overlap with the extension UI
120123 if ( style && Xss . FORBID_CSS_STYLE . test ( style ) ) {
121124 const updatedStyle = style . replace ( Xss . FORBID_CSS_STYLE , '' ) ;
122- ( node as HTMLElement ) . setAttribute ( 'style' , updatedStyle ) ;
125+ node . setAttribute ( 'style' , updatedStyle ) ;
123126 }
124127 }
125- if ( 'src' in node ) {
126- const img = node as HTMLImageElement ;
128+
129+ // Handle image attributes
130+ if ( node . tagName === 'IMG' ) {
131+ const img = node as HTMLImageElement ; // Narrow type to HTMLImageElement
127132 const src = img . getAttribute ( 'src' ) ;
128133 if ( imgHandling === 'IMG-DEL' ) {
129134 img . remove ( ) ; // just skip images
130135 } else if ( ! src ) {
131136 img . remove ( ) ; // src that exists but is null is suspicious
132137 } else if ( imgHandling === 'IMG-KEEP' && checkValidURL ( src ) ) {
133138 // replace remote image with remote_image_container
134- const remoteImgEl = `<div class="remote_image_container" data-src="${ src } " data-test="remote-image-container"><span>Authenticity of this remote image cannot be verified.</span></div>` ;
139+ const remoteImgEl = `
140+ <div class="remote_image_container" data-src="${ src } " data-test="remote-image-container">
141+ <span>Authenticity of this remote image cannot be verified.</span>
142+ </div>` ;
135143 Xss . replaceElementDANGEROUSLY ( img , remoteImgEl ) ; // xss-safe-value
136144 }
137145 }
146+
147+ // Handle custom containers or CID-patterned src
138148 if ( ( node . classList . contains ( 'remote_image_container' ) || CID_PATTERN . test ( node . getAttribute ( 'src' ) ?? '' ) ) && imgHandling === 'IMG-TO-PLAIN-TEXT' ) {
139- Xss . replaceElementDANGEROUSLY ( node , node . getAttribute ( 'data-src' ) ?? node . getAttribute ( 'alt' ) ?? '' ) ; // xss-safe-value
149+ const replacement = node . getAttribute ( 'data-src' ) ?? node . getAttribute ( 'alt' ) ?? '' ;
150+ Xss . replaceElementDANGEROUSLY ( node , replacement ) ; // xss-safe-value
140151 }
141- if ( 'target' in node ) {
142- // open links in new window
143- ( node as Element ) . setAttribute ( 'target' , '_blank' ) ;
144- // prevents https://www.owasp.org/index.php/Reverse_Tabnabbing
145- ( node as Element ) . setAttribute ( 'rel' , 'noopener noreferrer' ) ;
152+
153+ // Handle links (target and rel attributes)
154+ if ( node . tagName === 'A' ) {
155+ node . setAttribute ( 'target' , '_blank' ) ; // prevents https://www.owasp.org/index.php/Reverse_Tabnabbing
156+ node . setAttribute ( 'rel' , 'noopener noreferrer' ) ;
146157 }
147158 } ) ;
159+
148160 const cleanHtml = Xss . htmlSanitize ( dirtyHtml , true ) ;
149161 DOMPurify . removeAllHooks ( ) ;
150162 return cleanHtml ;
0 commit comments