diff --git a/frontend/lesson.html b/frontend/lesson.html index fa450ce..4aae0ff 100644 --- a/frontend/lesson.html +++ b/frontend/lesson.html @@ -897,6 +897,23 @@ /* ── helpers ── */ function escAll(s) { return String(s||'').replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"'); } + /* Санитайзер rich-HTML (блок columns хранит форматированный HTML из мини-редактора). + Парсим в инертный (картинки/скрипты НЕ исполняются), вырезаем опасное, + сериализуем обратно. Блокирует on*-обработчики, script/iframe, javascript:/data: URL. */ + function sanitizeRichHtml(html) { + const tpl = document.createElement('template'); + tpl.innerHTML = String(html || ''); + tpl.content.querySelectorAll('script,style,iframe,object,embed,link,meta,form,base').forEach(n => n.remove()); + tpl.content.querySelectorAll('*').forEach(el => { + for (const attr of Array.from(el.attributes)) { + const name = attr.name.toLowerCase(); + if (name.startsWith('on')) el.removeAttribute(attr.name); + else if (name === 'style') el.removeAttribute(attr.name); + else if (/^(href|src|xlink:href)$/.test(name) && /^\s*(javascript|data|vbscript):/i.test(attr.value || '')) el.removeAttribute(attr.name); + } + }); + return tpl.innerHTML; + } function fmtTime(s) { const d = new Date(s && s.includes('T') ? s : (s||'').replace(' ','T')+'Z'); const diff = Date.now() - d.getTime(); @@ -1376,7 +1393,7 @@ case 'columns': { const cols = Array.isArray(d.cols) ? d.cols : []; return ` - ${cols.map(c => `${c.content || ''}`).join('')} + ${cols.map(c => `${sanitizeRichHtml(c.content || '')}`).join('')} `; }