From 1ec52306a250bd9c4034ab62b6dac500fceabc24 Mon Sep 17 00:00:00 2001 From: Moshi <54333972+MoshiKoi@users.noreply.github.com> Date: Fri, 11 Aug 2023 02:13:54 -0700 Subject: [PATCH 1/4] Add notice when tags and attributes are rejected --- app/assets/javascripts/posts.js | 21 +++++++++++++++++++++ app/views/posts/_form.html.erb | 8 ++++++++ 2 files changed, 29 insertions(+) diff --git a/app/assets/javascripts/posts.js b/app/assets/javascripts/posts.js index 25c0fe65a..a30edb3d6 100644 --- a/app/assets/javascripts/posts.js +++ b/app/assets/javascripts/posts.js @@ -149,6 +149,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) + .find('ul') + .empty() + .append( + removedElements.map(name => $(`
  • <${name}>
  • `)), + removedAttributes.map(([attr, elName]) => $(`
  • ${attr} (in <${elName}>)
  • `))); + $tgt.parents('.form-group').siblings('.post-preview').html(html); $tgt.parents('form').find('.js-post-html[name="__html"]').val(html + ''); }, 0); diff --git a/app/views/posts/_form.html.erb b/app/views/posts/_form.html.erb index 47a8eaf77..40c533cb5 100644 --- a/app/views/posts/_form.html.erb +++ b/app/views/posts/_form.html.erb @@ -57,6 +57,14 @@ <%= render 'shared/body_field', f: f, field_name: :body_markdown, field_label: t('posts.body_label'), post: post, min_length: min_body_length(category), max_length: max_body_length(category) %> +
    +

    Unsupported HTML detected

    +

    The following HTML tags and attributes are unsupported and will be removed from the final post:

    + +

    For a list of allowed HTML, see this Meta post. + If you meant to display the tags as code in the post, please enclose them in a code block.

    +
    <% unless post_type.has_parent? %> From 27d8d77383418b75f1f479969c46ae3301130ef6 Mon Sep 17 00:00:00 2001 From: Moshi <54333972+MoshiKoi@users.noreply.github.com> Date: Wed, 4 Oct 2023 18:16:25 -0700 Subject: [PATCH 2/4] Link to help article --- app/views/posts/_form.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/posts/_form.html.erb b/app/views/posts/_form.html.erb index 40c533cb5..92a345698 100644 --- a/app/views/posts/_form.html.erb +++ b/app/views/posts/_form.html.erb @@ -62,7 +62,7 @@

    The following HTML tags and attributes are unsupported and will be removed from the final post:

    -

    For a list of allowed HTML, see this Meta post. +

    For a list of allowed HTML, see this help article. If you meant to display the tags as code in the post, please enclose them in a code block.

    From 5994e09df260561498d7143e92ebf65f5238723c Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Sun, 22 Oct 2023 02:25:54 +0300 Subject: [PATCH 3/4] fix .rejected-elements container staying hidden on no removed elements but some removed attributes --- app/assets/javascripts/posts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/posts.js b/app/assets/javascripts/posts.js index a30edb3d6..365aacce7 100644 --- a/app/assets/javascripts/posts.js +++ b/app/assets/javascripts/posts.js @@ -163,7 +163,7 @@ $(() => { $tgt.parents('form') .find('.rejected-elements') - .toggleClass('hide', removedElements.length === 0) + .toggleClass('hide', removedElements.length === 0 && removedAttributes.length === 0) .find('ul') .empty() .append( From 7bbf18b5c0048d0c6406e9ab64bcc8ebac7f4c69 Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Sun, 22 Oct 2023 03:03:52 +0300 Subject: [PATCH 4/4] add rowspan / colspan attribute validation --- app/assets/javascripts/posts.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/assets/javascripts/posts.js b/app/assets/javascripts/posts.js index 365aacce7..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);