From d3ab0d3e7631f0bcf649fc5b914581fe21fcdf38 Mon Sep 17 00:00:00 2001 From: Jukka Lampikoski Date: Fri, 13 Mar 2026 01:00:19 +0200 Subject: [PATCH] =?UTF-8?q?Korjaa=20HTML-render=C3=B6inti=20viesteiss?= =?UTF-8?q?=C3=A4=20+=20mailbox-valinta=20Zammad-tiketeille?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Sähköpostiviestien HTML renderöidään oikein (br, p, div, a, b, i jne.) sen sijaan että tagit näkyisivät raakatekstinä. Sanitoi vaarallisen sisällön (script, iframe, on*-attribuutit) pois turvallisesti. 2. Zammad-tiketeillä ei ole mailbox_id:tä, joten aiemmin valittiin aina listan ensimmäinen (tuki@serverihuone.com). Nyt näytetään "Valitse lähettäjä" placeholder kunnes käyttäjä valitsee oikean. Co-Authored-By: Claude Opus 4.6 --- index.html | 4 ++-- script.js | 30 ++++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index ccc57ee..73a51d4 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ Noxus HUB - + @@ -2229,6 +2229,6 @@ - + diff --git a/script.js b/script.js index 53e0a0b..040d9e3 100644 --- a/script.js +++ b/script.js @@ -528,6 +528,28 @@ function contractRemaining(sopimuskausi, alkupvm) { })(); function esc(str) { if (!str) return ''; const d = document.createElement('div'); d.textContent = str; return d.innerHTML; } +function sanitizeHtml(str) { + if (!str) return ''; + // Salli turvalliset tagit, poista kaikki muut + const allowed = ['br', 'p', 'div', 'a', 'b', 'i', 'strong', 'em', 'ul', 'ol', 'li', 'blockquote', 'h1', 'h2', 'h3', 'h4', 'span', 'table', 'tr', 'td', 'th', 'thead', 'tbody']; + const tmp = document.createElement('div'); + tmp.innerHTML = str; + // Poista script, style, iframe, object, embed, form tagit kokonaan + tmp.querySelectorAll('script,style,iframe,object,embed,form,input,textarea,button,link,meta').forEach(el => el.remove()); + // Poista on*-attribuutit kaikista elementeistä + tmp.querySelectorAll('*').forEach(el => { + [...el.attributes].forEach(attr => { + if (attr.name.startsWith('on') || attr.name === 'srcdoc') el.removeAttribute(attr.name); + }); + // Sanitoi href — vain http/https/mailto + if (el.hasAttribute('href')) { + const href = el.getAttribute('href') || ''; + if (!/^(https?:|mailto:)/i.test(href)) el.removeAttribute('href'); + } + }); + return tmp.innerHTML; +} + function timeAgo(dateStr) { if (!dateStr) return ''; const date = new Date(dateStr.replace(' ', 'T')); @@ -1800,7 +1822,7 @@ async function showTicketDetail(id, companyId = '') { ${esc(m.from_name || m.from)} ${esc(m.timestamp)} -
${esc(m.body)}
+
${m.type === 'email_in' || m.type === 'incoming' || m.type === 'outgoing' ? sanitizeHtml(m.body) : esc(m.body).replace(/\n/g, '
')}
`; }).join(''); @@ -1835,9 +1857,13 @@ async function showTicketDetail(id, companyId = '') { (!currentHiddenMailboxes.includes(String(mb.id)) && !currentHiddenMailboxes.includes(mb.id)) ); const ticketMbId = String(ticket.mailbox_id || ''); - mbSelect.innerHTML = visibleMailboxes.map(mb => + const hasMatch = ticketMbId && visibleMailboxes.some(mb => String(mb.id) === ticketMbId); + let optionsHtml = ''; + if (!hasMatch) optionsHtml = ''; + optionsHtml += visibleMailboxes.map(mb => `` ).join(''); + mbSelect.innerHTML = optionsHtml; // Vaihda allekirjoitusta kun mailbox vaihtuu mbSelect.addEventListener('change', function() { updateSignaturePreview(this.value);