class Rails::HTML::PermitScrubber

Rails::HTML::PermitScrubber

Rails::HTML::PermitScrubber allows you to permit only your own tags and/or attributes.

Rails::HTML::PermitScrubber can be subclassed to determine:

Subclasses don't need to worry if tags or attributes are set or not. If tags or attributes are not set, Loofah's behavior will be used. If you override allowed_node? and no tags are set, it will not be called. Instead Loofahs behavior will be used. Likewise for scrub_attribute? and attributes respectively.

Text and CDATA nodes are skipped by default. Unallowed elements will be stripped, i.e. element is removed but its subtree kept. Supplied tags and attributes should be Enumerables.

tags= If set, elements excluded will be stripped. If not, elements are stripped based on Loofahs HTML5::Scrub.allowed_element?.

attributes= If set, attributes excluded will be removed. If not, attributes are removed based on Loofahs HTML5::Scrub.scrub_attributes.

class CommentScrubber < Rails::HTML::PermitScrubber
  def initialize
    super
    self.tags = %w(form script comment blockquote)
  end

  def skip_node?(node)
    node.text?
  end

  def scrub_attribute?(name)
    name == "style"
  end
end

See the documentation for Nokogiri::XML::Node to understand what's possible with nodes: nokogiri.org/rdoc/Nokogiri/XML/Node.html

Attributes

attributes[R]
prune[R]
tags[R]

Public Class Methods

new(prune: false) click to toggle source
# File lib/rails/html/scrubbers.rb, line 52
def initialize(prune: false)
  @prune = prune
  @direction = @prune ? :top_down : :bottom_up
  @tags, @attributes = nil, nil
end

Public Instance Methods

attributes=(attributes) click to toggle source
# File lib/rails/html/scrubbers.rb, line 62
def attributes=(attributes)
  @attributes = validate!(attributes.dup, :attributes)
end
scrub(node) click to toggle source
# File lib/rails/html/scrubbers.rb, line 66
def scrub(node)
  if Loofah::HTML5::Scrub.cdata_needs_escaping?(node)
    replacement = Loofah::HTML5::Scrub.cdata_escape(node)
    node.replace(replacement)
    return CONTINUE
  end
  return CONTINUE if skip_node?(node)

  unless (node.element? || node.comment?) && keep_node?(node)
    return STOP unless scrub_node(node) == CONTINUE
  end

  scrub_attributes(node)
  CONTINUE
end
tags=(tags) click to toggle source
# File lib/rails/html/scrubbers.rb, line 58
def tags=(tags)
  @tags = validate!(tags.dup, :tags)
end

Protected Instance Methods

allowed_node?(node) click to toggle source
# File lib/rails/html/scrubbers.rb, line 83
def allowed_node?(node)
  @tags.include?(node.name)
end
keep_node?(node) click to toggle source
# File lib/rails/html/scrubbers.rb, line 95
def keep_node?(node)
  if @tags
    allowed_node?(node)
  else
    Loofah::HTML5::Scrub.allowed_element?(node.name)
  end
end
scrub_attribute(node, attr_node) click to toggle source
# File lib/rails/html/scrubbers.rb, line 162
def scrub_attribute(node, attr_node)
  attr_name = if attr_node.namespace
    "#{attr_node.namespace.prefix}:#{attr_node.node_name}"
  else
    attr_node.node_name
  end

  return if Loofah::HTML5::SafeList::ATTR_VAL_IS_URI.include?(attr_name) && Loofah::HTML5::Scrub.scrub_uri_attribute(attr_node)

  if Loofah::HTML5::SafeList::SVG_ATTR_VAL_ALLOWS_REF.include?(attr_name)
    Loofah::HTML5::Scrub.scrub_attribute_that_allows_local_ref(attr_node)
  end

  if Loofah::HTML5::SafeList::SVG_ALLOW_LOCAL_HREF.include?(node.name) && attr_name == "xlink:href" && attr_node.value =~ /^\s*[^#\s].*/m
    attr_node.remove
  end

  node.remove_attribute(attr_node.name) if attr_name == "src" && attr_node.value !~ /[^[:space:]]/

  Loofah::HTML5::Scrub.force_correct_attribute_escaping! node
end
scrub_attribute?(name) click to toggle source
# File lib/rails/html/scrubbers.rb, line 91
def scrub_attribute?(name)
  !@attributes.include?(name)
end
scrub_attributes(node) click to toggle source
# File lib/rails/html/scrubbers.rb, line 112
def scrub_attributes(node)
  if @attributes
    node.attribute_nodes.each do |attr|
      if scrub_attribute?(attr.name)
        attr.remove
      else
        scrub_attribute(node, attr)
      end
    end

    scrub_css_attribute(node)
  else
    Loofah::HTML5::Scrub.scrub_attributes(node)
  end
end
scrub_css_attribute(node) click to toggle source
# File lib/rails/html/scrubbers.rb, line 128
def scrub_css_attribute(node)
  if Loofah::HTML5::Scrub.respond_to?(:scrub_css_attribute)
    Loofah::HTML5::Scrub.scrub_css_attribute(node)
  else
    style = node.attributes["style"]
    style.value = Loofah::HTML5::Scrub.scrub_css(style.value) if style
  end
end
scrub_node(node) click to toggle source
# File lib/rails/html/scrubbers.rb, line 103
def scrub_node(node)
  # If a node has a namespace, then it's a tag in either a `math` or `svg` foreign context,
  # and we should always prune it to avoid namespace confusion and mutation XSS vectors.
  unless prune || node.namespace
    node.before(node.children)
  end
  node.remove
end
skip_node?(node) click to toggle source
# File lib/rails/html/scrubbers.rb, line 87
def skip_node?(node)
  node.text?
end
validate!(var, name) click to toggle source
# File lib/rails/html/scrubbers.rb, line 137
def validate!(var, name)
  if var && !var.is_a?(Enumerable)
    raise ArgumentError, "You should pass :#{name} as an Enumerable"
  end

  if var && name == :tags
    if var.include?("mglyph")
      warn("WARNING: 'mglyph' tags cannot be allowed by the PermitScrubber and will be scrubbed")
      var.delete("mglyph")
    end

    if var.include?("malignmark")
      warn("WARNING: 'malignmark' tags cannot be allowed by the PermitScrubber and will be scrubbed")
      var.delete("malignmark")
    end

    if var.include?("noscript")
      warn("WARNING: 'noscript' tags cannot be allowed by the PermitScrubber and will be scrubbed")
      var.delete("noscript")
    end
  end

  var
end