Calendar + date cards: render webhook HTML (sanitize, links open in new tab)
This commit is contained in:
@@ -1497,6 +1497,8 @@ TELEGRAM BANNER
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.calendar-event-headline { flex: 1; color: #1c1e21; }
|
.calendar-event-headline { flex: 1; color: #1c1e21; }
|
||||||
|
.calendar-event-headline a { color: #1877f2; text-decoration: none; font-weight: 600; }
|
||||||
|
.calendar-event-headline a:hover { text-decoration: underline; }
|
||||||
.calendar-event-time { color: #65676b; white-space: nowrap; }
|
.calendar-event-time { color: #65676b; white-space: nowrap; }
|
||||||
|
|
||||||
/* ========================================
|
/* ========================================
|
||||||
|
|||||||
+30
-2
@@ -981,7 +981,7 @@ function renderDates(events) {
|
|||||||
|
|
||||||
card.innerHTML = '<div class="date-header">' +
|
card.innerHTML = '<div class="date-header">' +
|
||||||
'<div class="date-icon">📅</div>' +
|
'<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><div class="date-info">' +
|
||||||
'<div class="date-row">' +
|
'<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>' +
|
'<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'); }
|
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() {
|
function initCalendar() {
|
||||||
renderCalendar();
|
renderCalendar();
|
||||||
}
|
}
|
||||||
@@ -1028,7 +1056,7 @@ function renderCalendar() {
|
|||||||
const timeStr = pad2(sd.getHours()) + ':' + pad2(sd.getMinutes()) +
|
const timeStr = pad2(sd.getHours()) + ':' + pad2(sd.getMinutes()) +
|
||||||
(ed ? '–' + pad2(ed.getHours()) + ':' + pad2(ed.getMinutes()) : '');
|
(ed ? '–' + pad2(ed.getHours()) + ':' + pad2(ed.getMinutes()) : '');
|
||||||
list += '<li><span class="calendar-event-date">' + dateStr + '</span>' +
|
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>';
|
'<span class="calendar-event-time">' + timeStr + '</span></li>';
|
||||||
});
|
});
|
||||||
list += '</ul>';
|
list += '</ul>';
|
||||||
|
|||||||
Reference in New Issue
Block a user