@@ -141,7 +141,13 @@ function escapeTemplateString(value: string) {
141141}
142142
143143function makeSafeTemplate ( raw : string ) {
144- return `{{ Safe "${ escapeTemplateString ( raw ) } " }}` ;
144+ // Encode angle brackets so DOMParser does not consume Outlook conditional comments
145+ // before the Go template expression is evaluated.
146+ const escaped = escapeTemplateString ( raw )
147+ . replace ( / < / g, '\\x3c' )
148+ . replace ( / > / g, '\\x3e' ) ;
149+
150+ return `{{ Safe "${ escaped } " }}` ;
145151}
146152
147153function getWrapperOptions ( style : string | null ) {
@@ -155,7 +161,9 @@ function getWrapperOptions(style: string | null) {
155161}
156162
157163function buildPresentationTable ( contents : string , width : string = '100%' ) {
158- return `<table role="presentation" width="${ width } " cellpadding="0" cellspacing="0" border="0" style="${ PRESENTATION_TABLE_STYLE } ">${ contents } </table>` ;
164+ const widthAttr = width && width !== 'auto' ? ` width="${ escapeAttribute ( width ) } "` : '' ;
165+
166+ return `<table role="presentation"${ widthAttr } cellpadding="0" cellspacing="0" border="0" style="${ PRESENTATION_TABLE_STYLE } ">${ contents } </table>` ;
159167}
160168
161169function hasSingleChildMatching ( div : HTMLDivElement , predicate : ( child : Element ) => boolean ) {
@@ -326,12 +334,14 @@ function buildBulletproofButton(anchor: HTMLAnchorElement, wrapperStyle: string)
326334 const estimatedHeight = Math . max ( lineHeight + paddingValues . top + paddingValues . bottom , 32 ) ;
327335 const arcsize = Math . max ( 0 , Math . min ( 50 , Math . round ( ( borderRadius / estimatedHeight ) * 100 ) ) ) ;
328336 const cleanAnchorStyle = anchor . getAttribute ( 'style' ) || '' ;
329- const vml = makeSafeTemplate ( `<!--[if mso]><v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="${ escapeAttribute ( href ) } " style="height:${ estimatedHeight } px;v-text-anchor:middle;width:${ estimatedWidth } px;" arcsize="${ arcsize } %" strokecolor="${ escapeAttribute ( buttonColor ) } " fillcolor="${ escapeAttribute ( buttonColor ) } "><w:anchorlock/><center style="color:${ escapeAttribute ( textColor ) } ;font-family:${ escapeAttribute ( fontFamily ) } ;font-size:${ fontSize } px;font-weight:${ escapeAttribute ( fontWeight ) } ;">${ escapeHtml ( text ) } </center></v:roundrect><![endif]-->` ) ;
337+ const msoStart = makeSafeTemplate ( '<!--[if mso]>' ) ;
338+ const msoEnd = makeSafeTemplate ( '<![endif]-->' ) ;
339+ const vml = `<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="${ escapeAttribute ( href ) } " style="height:${ estimatedHeight } px;v-text-anchor:middle;width:${ estimatedWidth } px;" arcsize="${ arcsize } %" strokecolor="${ escapeAttribute ( buttonColor ) } " fillcolor="${ escapeAttribute ( buttonColor ) } "><w:anchorlock/><center style="color:${ escapeAttribute ( textColor ) } ;font-family:${ escapeAttribute ( fontFamily ) } ;font-size:${ fontSize } px;font-weight:${ escapeAttribute ( fontWeight ) } ;">${ escapeHtml ( text ) } </center></v:roundrect>` ;
330340 const nonMsoStart = makeSafeTemplate ( '<!--[if !mso]><!-->' ) ;
331341 const nonMsoEnd = makeSafeTemplate ( '<!--<![endif]-->' ) ;
332342
333343 return buildPresentationTable (
334- `<tbody><tr><td align="${ escapeAttribute ( align ) } " style="${ escapeAttribute ( wrapperStyle ) } ">${ vml } ${ nonMsoStart } <a href="${ escapeAttribute ( href ) } "${ targetAttr } style="${ escapeAttribute ( cleanAnchorStyle ) } ">${ escapeHtml ( text ) } </a>${ nonMsoEnd } </td></tr></tbody>`
344+ `<tbody><tr><td align="${ escapeAttribute ( align ) } " style="${ escapeAttribute ( wrapperStyle ) } ">${ msoStart } ${ vml } ${ msoEnd } ${ nonMsoStart } <a href="${ escapeAttribute ( href ) } "${ targetAttr } style="${ escapeAttribute ( cleanAnchorStyle ) } ">${ escapeHtml ( text ) } </a>${ nonMsoEnd } </td></tr></tbody>`
335345 ) ;
336346}
337347
0 commit comments