${escapeMessageText(message.text)}
diff --git a/static/js/rsvp-tracker.js b/static/js/rsvp-tracker.js
index 4fb035e..e0d5f91 100644
--- a/static/js/rsvp-tracker.js
+++ b/static/js/rsvp-tracker.js
@@ -1,3 +1,12 @@
+function escapeHtml(s) {
+ return String(s)
+ .replaceAll('&', '&')
+ .replaceAll('<', '<')
+ .replaceAll('>', '>')
+ .replaceAll('"', '"')
+ .replaceAll("'", ''');
+}
+
// RSVP response patterns (order matters - check longer phrases first!)
const RESPONSE_PATTERNS = {
'no': ['kommer inte', 'no', 'nej', 'not attending', 'kan inte', 'cannot attend'],
@@ -88,13 +97,13 @@ function generateAttendeeList(attendees) {
- ${attendee.shortName.substring(0, 4)}
+ ${escapeHtml(attendee.shortName.substring(0, 4))}
diff --git a/static/js/status/channel-utilization-gauges.js b/static/js/status/channel-utilization-gauges.js
index 897e5c3..1773d64 100644
--- a/static/js/status/channel-utilization-gauges.js
+++ b/static/js/status/channel-utilization-gauges.js
@@ -1,3 +1,12 @@
+function escapeHtml(s) {
+ return String(s)
+ .replaceAll('&', '&')
+ .replaceAll('<', '<')
+ .replaceAll('>', '>')
+ .replaceAll('"', '"')
+ .replaceAll("'", ''');
+}
+
async function channelUtilizationGauges() {
try {
await fetchNodes();
@@ -171,9 +180,9 @@ function showTop10NodesLegend(channelName, top10Nodes) {
${node.utilizationValue.toFixed(1)}%
- [${shortName}]
-
- ${longName}
+ [${escapeHtml(shortName)}]
+
+ ${escapeHtml(longName)}
`;