Calendar + date cards: render webhook HTML (sanitize, links open in new tab)
This commit is contained in:
+30
-2
@@ -981,7 +981,7 @@ function renderDates(events) {
|
||||
|
||||
card.innerHTML = '<div class="date-header">' +
|
||||
'<div class="date-icon">📅</div>' +
|
||||
'<h3 class="date-headline">' + escapeHtml(event.headline || 'Event') + '</h3>' +
|
||||
'<h3 class="date-headline">' + sanitizeHtml(event.headline || 'Event') + '</h3>' +
|
||||
'</div><div class="date-info">' +
|
||||
'<div class="date-row">' +
|
||||
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>' +
|
||||
@@ -1004,6 +1004,34 @@ function renderDates(events) {
|
||||
// ========================================
|
||||
function pad2(n) { return String(n).padStart(2, '0'); }
|
||||
|
||||
// Webhook text may contain simple HTML (e.g. links). Sanitize before injecting:
|
||||
// strip script/iframe/... tags, on* handlers and javascript: URLs; force
|
||||
// links to open in a new tab.
|
||||
function sanitizeHtml(html) {
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = String(html || '');
|
||||
div.querySelectorAll('script, style, iframe, object, embed, link, meta').forEach(n => n.remove());
|
||||
const walk = (node) => {
|
||||
Array.from(node.childNodes || []).forEach(child => {
|
||||
if (child.nodeType !== 1) return;
|
||||
Array.from(child.attributes || []).forEach(attr => {
|
||||
const name = attr.name.toLowerCase();
|
||||
const val = (attr.value || '').replace(/\s+/g, '').toLowerCase();
|
||||
if (name.startsWith('on') || ((name === 'href' || name === 'src') && val.startsWith('javascript:'))) {
|
||||
child.removeAttribute(attr.name);
|
||||
}
|
||||
});
|
||||
if (child.tagName === 'A') {
|
||||
child.setAttribute('target', '_blank');
|
||||
child.setAttribute('rel', 'noopener');
|
||||
}
|
||||
walk(child);
|
||||
});
|
||||
};
|
||||
walk(div);
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function initCalendar() {
|
||||
renderCalendar();
|
||||
}
|
||||
@@ -1028,7 +1056,7 @@ function renderCalendar() {
|
||||
const timeStr = pad2(sd.getHours()) + ':' + pad2(sd.getMinutes()) +
|
||||
(ed ? '–' + pad2(ed.getHours()) + ':' + pad2(ed.getMinutes()) : '');
|
||||
list += '<li><span class="calendar-event-date">' + dateStr + '</span>' +
|
||||
'<span class="calendar-event-headline">' + escapeHtml(ev.headline || 'Event') + '</span>' +
|
||||
'<span class="calendar-event-headline">' + sanitizeHtml(ev.headline || 'Event') + '</span>' +
|
||||
'<span class="calendar-event-time">' + timeStr + '</span></li>';
|
||||
});
|
||||
list += '</ul>';
|
||||
|
||||
Reference in New Issue
Block a user