increments/qiita-markdown

View on GitHub
lib/qiita/markdown/filters/final_sanitizer.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
95%
# frozen_string_literal: true

module Qiita
  module Markdown
    module Filters
      # Sanitizes undesirable elements by whitelist-based rule.
      # You can pass optional :rule and :script context.
      #
      # Since this filter is applied at the end of html-pipeline, it's rules
      # are intentionally weakened to allow elements and attributes which are
      # generated by other filters.
      #
      # @see Qiita::Markdown::Filters::UserInputSanitizerr
      class FinalSanitizer < ::HTML::Pipeline::Filter
        RULE = {
          attributes: {
            "a" => %w[
              data-hovercard-target-name
              data-hovercard-target-type
              href
              rel
            ],
            "blockquote" => Embed::Tweet::ATTRIBUTES,
            "iframe" => %w[
              allowfullscreen
              frameborder
              height
              loading
              marginheight
              marginwidth
              scrolling
              src
              style
              width
            ],
            "img" => [
              "src",
            ],
            "input" => %w[
              checked
              disabled
              type
            ],
            "div" => %w[
              itemscope
              itemtype
            ],
            "p" => Embed::CodePen::ATTRIBUTES,
            "script" => %w[
              async
              src
              type
            ].concat(
              Embed::SpeekerDeck::ATTRIBUTES,
              Embed::Docswell::ATTRIBUTES,
            ),
            "span" => [
              "style",
            ],
            "td" => [
              "style",
            ],
            "th" => [
              "style",
            ],
            "details" => [
              "open",
            ],
            "video" => %w[
              src
              autoplay
              controls
              loop
              muted
              poster
            ],
            all: %w[
              abbr
              align
              alt
              border
              cellpadding
              cellspacing
              cite
              class
              color
              cols
              colspan
              data-lang
              data-sourcepos
              datetime
              height
              hreflang
              id
              itemprop
              lang
              name
              rowspan
              tabindex
              target
              title
              width
            ],
          },
          css: {
            properties: %w[
              background-color
              border
              text-align
            ],
          },
          elements: %w[
            a
            b
            blockquote
            br
            caption
            code
            dd
            del
            details
            div
            dl
            dt
            em
            font
            h1
            h2
            h3
            h4
            h5
            h6
            h7
            h8
            hr
            i
            img
            input
            ins
            kbd
            li
            ol
            p
            pre
            q
            rp
            rt
            ruby
            s
            samp
            script
            iframe
            section
            span
            strike
            strong
            sub
            summary
            sup
            table
            tbody
            td
            tfoot
            th
            thead
            tr
            tt
            ul
            var
          ],
          protocols: {
            "a" => {
              "href" => [
                :relative,
                "http",
                "https",
                "mailto",
              ],
            },
            "img" => {
              "src" => [
                :relative,
                "http",
                "https",
              ],
            },
            "video" => {
              "src" => [
                :relative,
                "http",
                "https",
              ],
              "poster" => [
                :relative,
                "http",
                "https",
              ],
            },
          },
          transformers: [
            Transformers::StripInvalidNode,
            Transformers::FilterScript,
            Transformers::FilterIframe,
          ],
        }.freeze

        SCRIPTABLE_RULE = RULE.dup.tap do |rule|
          rule[:attributes] = RULE[:attributes].dup
          rule[:attributes][:all] = rule[:attributes][:all] + [:data]
          rule[:elements] = RULE[:elements] + ["video"]
          rule[:transformers] = rule[:transformers] - [Transformers::FilterScript, Transformers::FilterIframe]
        end.freeze

        def call
          ::Sanitize.clean_node!(doc, rule)
          doc
        end

        private

        def has_script_context?
          context[:script] == true
        end

        def rule
          case
          when context[:rule]
            context[:rule]
          when has_script_context?
            SCRIPTABLE_RULE
          else
            RULE
          end
        end
      end
    end
  end
end