fix(security): убрать stored-XSS в блоке columns урока (Спринт1 #4)
Блок columns хранит rich-HTML из мини-редактора и рендерился сырым в innerHTML (единственный неэкранированный блок) — учитель мог внедрить <img onerror>/script, исполняемый у каждого ученика (кража JWT из localStorage). Добавлен санитайзер sanitizeRichHtml (инертный template + вырезание on*/script/iframe/javascript:), сохраняет форматирование, но блокирует исполнение. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+18
-1
@@ -897,6 +897,23 @@
|
||||
|
||||
/* ── helpers ── */
|
||||
function escAll(s) { return String(s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); }
|
||||
/* Санитайзер rich-HTML (блок columns хранит форматированный HTML из мини-редактора).
|
||||
Парсим в инертный <template> (картинки/скрипты НЕ исполняются), вырезаем опасное,
|
||||
сериализуем обратно. Блокирует 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 `<div class="lesson-block block-columns cols-${cols.length}">
|
||||
${cols.map(c => `<div class="block-col">${c.content || ''}</div>`).join('')}
|
||||
${cols.map(c => `<div class="block-col">${sanitizeRichHtml(c.content || '')}</div>`).join('')}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user