diff --git a/app/assets/javascripts/posts.js b/app/assets/javascripts/posts.js index 25c0fe65a..955587c4a 100644 --- a/app/assets/javascripts/posts.js +++ b/app/assets/javascripts/posts.js @@ -6,6 +6,19 @@ const ALLOWED_ATTR = ['id', 'class', 'href', 'title', 'src', 'height', 'width', 'start', 'dir']; $(() => { + DOMPurify.addHook("uponSanitizeAttribute", (node, event) => { + const rowspan = node.getAttribute("rowspan"); + const colspan = node.getAttribute("colspan"); + + if (rowspan && Number.isNaN(+rowspan)) { + event.keepAttr = false; + } + + if (colspan && Number.isNaN(+colspan)) { + event.keepAttr = false; + } + }); + const $uploadForm = $('.js-upload-form'); const stringInsert = (str, idx, insert) => str.slice(0, idx) + insert + str.slice(idx); @@ -149,6 +162,27 @@ $(() => { ALLOWED_TAGS, ALLOWED_ATTR }); + + const removedElements = [...new Set(DOMPurify.removed + .filter(entry => entry.element && !(entry.element instanceof HTMLBodyElement)) + .map(entry => entry.element.localName))]; + + const removedAttributes = [...new Set(DOMPurify.removed + .filter(entry => entry.attribute) + .map(entry => [ + entry.attribute.name + (entry.attribute.value ? `='${entry.attribute.value}'` : ''), + entry.from.localName + ]))] + + $tgt.parents('form') + .find('.rejected-elements') + .toggleClass('hide', removedElements.length === 0 && removedAttributes.length === 0) + .find('ul') + .empty() + .append( + removedElements.map(name => $(`
<${name}>
${attr}
(in <${elName}>
)