diff --git a/.common/utils/helpers.js b/.common/utils/helpers.js
index c89b721..40d508e 100644
--- a/.common/utils/helpers.js
+++ b/.common/utils/helpers.js
@@ -178,6 +178,14 @@ function DecodeHTMLString(html) {
return txt.value;
}
+// Simple HTML escape function to prevent XSS attacks
+function EscapeHTML(str) {
+ return str.replace(/[&<>"']/g, match => {
+ const escape = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
+ return escape[match];
+ });
+}
+
function TranslateToFurry(sentence) {
// Split on
tags, keeping them in the result
const parts = sentence.split(/(
]*>)/gi);
@@ -273,26 +281,26 @@ function RandomHex(str) {
function ConstructMessageFromParts(parts) {
return parts.map(part => {
if (part.emoji)
- return `
`;
+ return `
`;
if (!part.type)
- return part.text;
+ return EscapeHTML(part.text);
if (part.source == 'Twemoji')
- return part.text;
+ return EscapeHTML(part.text);
switch (part.type)
{
case "text":
- return part.text;
+ return EscapeHTML(part.text);
case "cheer":
// Render the cheer emote image
- const emoteImg = `
`;
+ const emoteImg = `
`;
// Render the bits count
- const bitLabel = `${part.bits}`;
+ const bitLabel = `${EscapeHTML(part.bits.toString())}`;
return emoteImg + bitLabel;
default:
- return `
`;
+ return `
`;
}
}).join('');
}
@@ -310,27 +318,43 @@ function RenderMessageWithEmotesHTML(originalMessage, emotes) {
emotes.forEach(emote => {
// Add text before the emote
if (emote.startIndex > cursor) {
- html += escapeHTML(originalMessage.slice(cursor, emote.startIndex));
+ html += EscapeHTML(originalMessage.slice(cursor, emote.startIndex));
}
// Add emote image
- html += `
`;
+ html += `
`;
cursor = emote.endIndex + 1;
});
// Add remaining text after last emote
if (cursor < originalMessage.length) {
- html += escapeHTML(originalMessage.slice(cursor));
- }
-
- // Simple HTML escape function
- function escapeHTML(str) {
- return str.replace(/[&<>"']/g, match => {
- const escape = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
- return escape[match];
- });
+ html += EscapeHTML(originalMessage.slice(cursor));
}
return html;
+}
+
+async function LoadHTMLTemplate(name) {
+ const response = await fetch(name);
+
+ if (!response.ok) {
+ throw new Error(`Failed to load template: ${name}`);
+ }
+
+ const html = await response.text();
+
+ const tempDiv = document.createElement("div");
+ tempDiv.innerHTML = html;
+
+ const template = tempDiv.querySelector("template");
+
+ if (!template) {
+ throw new Error(`No found in ${name}.html`);
+ }
+
+ // Avoid duplicate registration
+ if (!document.getElementById(template.id)) {
+ document.body.appendChild(template.cloneNode(true));
+ }
}
\ No newline at end of file