Korjaa HTML-renderöinti viesteissä + mailbox-valinta Zammad-tiketeille

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 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 01:00:19 +02:00
parent 109dce3f26
commit d3ab0d3e76
2 changed files with 30 additions and 4 deletions

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Noxus HUB</title>
<link rel="stylesheet" href="style.css?v=20260313">
<link rel="stylesheet" href="style.css?v=20260313b">
</head>
<body>
<!-- Login -->
@@ -2229,6 +2229,6 @@
</div>
</div>
<script src="script.js?v=20260313"></script>
<script src="script.js?v=20260313b"></script>
</body>
</html>

View File

@@ -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 = '') {
<strong>${esc(m.from_name || m.from)}</strong>
<span class="ticket-msg-time">${esc(m.timestamp)}</span>
</div>
<div class="ticket-msg-body">${esc(m.body)}</div>
<div class="ticket-msg-body">${m.type === 'email_in' || m.type === 'incoming' || m.type === 'outgoing' ? sanitizeHtml(m.body) : esc(m.body).replace(/\n/g, '<br>')}</div>
</div>`;
}).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 = '<option value="" selected>— Valitse lähettäjä —</option>';
optionsHtml += visibleMailboxes.map(mb =>
`<option value="${esc(mb.id)}" ${String(mb.id) === ticketMbId ? 'selected' : ''}>${esc(mb.nimi || mb.smtp_from_email)} &lt;${esc(mb.smtp_from_email)}&gt;</option>`
).join('');
mbSelect.innerHTML = optionsHtml;
// Vaihda allekirjoitusta kun mailbox vaihtuu
mbSelect.addEventListener('change', function() {
updateSignaturePreview(this.value);