-
-
Notifications
You must be signed in to change notification settings - Fork 80
Updated html markup for CTA card frontend rendering #1442
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
57244a7
8eec344
37897fb
aece70e
059302d
ac64145
c5c512d
96df53b
19c7141
3eb9140
3777a50
31dcacd
b8ee167
d028e71
8e66d4f
dc8346b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,56 +3,139 @@ import {renderWithVisibility} from '../../utils/visibility'; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO - this is a placeholder for the cta card web template | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function ctaCardTemplate(dataset) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const backgroundAccent = dataset.backgroundColor === 'accent' ? 'kg-style-accent' : ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Add validation for buttonColor | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!dataset.buttonColor || !dataset.buttonColor.match(/^[a-zA-Z\d-]+$/)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dataset.buttonColor = 'accent'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const buttonAccent = dataset.buttonColor === 'accent' ? 'kg-style-accent' : ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const buttonStyle = dataset.buttonColor !== 'accent' ? `background-color: ${dataset.buttonColor};` : ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const buttonStyle = dataset.buttonColor === 'accent' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? `style="color: ${dataset.buttonTextColor};"` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : `style="background-color: ${dataset.buttonColor}; color: ${dataset.buttonTextColor};"`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const buttonStyle = dataset.buttonColor === 'accent' | |
| ? `style="color: ${dataset.buttonTextColor};"` | |
| : `style="background-color: ${dataset.buttonColor}; color: ${dataset.buttonTextColor};"`; | |
| const sanitizeColor = (color) => { | |
| // Implement color value sanitization | |
| return color.replace(/[^#,().\d\w%-]/g, ''); | |
| }; | |
| const buttonStyle = dataset.buttonColor === 'accent' | |
| ? `style="color: ${sanitizeColor(dataset.buttonTextColor)};"` | |
| : `style="background-color: ${sanitizeColor(dataset.buttonColor)}; color: ${sanitizeColor(dataset.buttonTextColor)};"`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enhance security and accessibility.
- URLs are directly interpolated without sanitization, which could lead to XSS vulnerabilities.
- The image alt text is generic and not customizable, which impacts accessibility.
+ const sanitizeUrl = (url) => {
+ try {
+ const parsed = new URL(url);
+ return parsed.toString();
+ } catch {
+ return '#';
+ }
+ };
+
return `
<div class="kg-card kg-cta-card kg-cta-bg-${dataset.backgroundColor} kg-cta-${dataset.layout}" data-layout="${dataset.layout}">
${dataset.hasSponsorLabel ? `
<div class="kg-cta-sponsor-label">
Sponsored
</div>
` : ''}
<div class="kg-cta-content">
${dataset.hasImage ? `
<div class="kg-cta-image-container">
- <img src="${dataset.imageUrl}" alt="CTA Image">
+ <img src="${sanitizeUrl(dataset.imageUrl)}" alt="${dataset.imageAltText || 'CTA Image'}">
</div>
` : ''}
<div class="kg-cta-content-inner">
<div class="kg-cta-text">
${dataset.textValue}
</div>
${dataset.showButton ? `
- <a href="${dataset.buttonUrl}" class="kg-cta-button ${buttonAccent}"
+ <a href="${sanitizeUrl(dataset.buttonUrl)}" class="kg-cta-button ${buttonAccent}"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div class="kg-card kg-cta-card kg-cta-bg-${dataset.backgroundColor} kg-cta-${dataset.layout}" data-layout="${dataset.layout}"> | |
| ${dataset.hasSponsorLabel ? ` | |
| <div class="kg-sponsor-label"> | |
| <div class="kg-cta-sponsor-label"> | |
| Sponsored | |
| </div> | |
| ` : ''} | |
| <div class="kg-cta-content"> | |
| ${dataset.hasImage ? ` | |
| <div class="kg-cta-image-container"> | |
| <img src="${dataset.imageUrl}" alt="CTA Image"> | |
| </div> | |
| ` : ''} | |
| <div class="kg-cta-content-inner"> | |
| <div class="kg-cta-text"> | |
| ${dataset.textValue} | |
| </div> | |
| ${dataset.showButton ? ` | |
| <a href="${dataset.buttonUrl}" class="kg-cta-button ${buttonAccent}" | |
| ${buttonStyle}> | |
| ${dataset.buttonText} | |
| </a> | |
| ` : ''} | |
| </div> | |
| </div> | |
| </div> | |
| const sanitizeUrl = (url) => { | |
| try { | |
| const parsed = new URL(url); | |
| return parsed.toString(); | |
| } catch { | |
| return '#'; | |
| } | |
| }; | |
| return ` | |
| <div class="kg-card kg-cta-card kg-cta-bg-${dataset.backgroundColor} kg-cta-${dataset.layout}" data-layout="${dataset.layout}"> | |
| ${dataset.hasSponsorLabel ? ` | |
| <div class="kg-cta-sponsor-label"> | |
| Sponsored | |
| </div> | |
| ` : ''} | |
| <div class="kg-cta-content"> | |
| ${dataset.hasImage ? ` | |
| <div class="kg-cta-image-container"> | |
| <img src="${sanitizeUrl(dataset.imageUrl)}" alt="${dataset.imageAltText || 'CTA Image'}"> | |
| </div> | |
| ` : ''} | |
| <div class="kg-cta-content-inner"> | |
| <div class="kg-cta-text"> | |
| ${dataset.textValue} | |
| </div> | |
| ${dataset.showButton ? ` | |
| <a href="${sanitizeUrl(dataset.buttonUrl)}" class="kg-cta-button ${buttonAccent}" | |
| ${buttonStyle}> | |
| ${dataset.buttonText} | |
| </a> | |
| ` : ''} | |
| </div> | |
| </div> | |
| </div> | |
| `; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Refactor email template for maintainability and security.
The email template has several issues:
- Same security vulnerabilities as the web template (unsanitized URLs and colors)
- Complex nested table structure with duplicated code between layouts
Consider these improvements:
- Extract common table structures into reusable functions:
const renderImageSection = (imageUrl, altText, minimal = false) => `
<tr>
<td class="kg-cta-image-container"${minimal ? ' width="64"' : ''}>
<img src="${sanitizeUrl(imageUrl)}"
alt="${altText || 'CTA Image'}"
class="kg-cta-image"
${minimal ? ' width="64"' : ''}>
</td>
</tr>
`;- Apply the same security measures as the web template:
-<img src="${dataset.imageUrl}" alt="CTA Image" class="kg-cta-image">
+<img src="${sanitizeUrl(dataset.imageUrl)}" alt="${dataset.imageAltText || 'CTA Image'}" class="kg-cta-image">
-<a href="${dataset.buttonUrl}"
+<a href="${sanitizeUrl(dataset.buttonUrl)}" There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Align backgroundColor validation with buttonColor validation.
The validation pattern for backgroundColor is more restrictive than buttonColor. Consider using the same pattern for consistency.
-if (!dataset.backgroundColor || !dataset.backgroundColor.match(/^[a-zA-Z\d-]+$/)) {
+if (!dataset.backgroundColor || !dataset.backgroundColor.match(/^[a-zA-Z\d-]+|#([a-fA-F\d]{3}|[a-fA-F\d]{6})$/)) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Add validation for backgroundColor | |
| if (!dataset.backgroundColor || !dataset.backgroundColor.match(/^[a-zA-Z\d-]+$/)) { | |
| dataset.backgroundColor = 'white'; | |
| } | |
| // Add validation for backgroundColor | |
| if (!dataset.backgroundColor || !dataset.backgroundColor.match(/^[a-zA-Z\d-]+|#([a-fA-F\d]{3}|[a-fA-F\d]{6})$/)) { | |
| dataset.backgroundColor = 'white'; | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this can be removed now 😄