Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

experiment: use loofah attribute scrubber to explore functional drift #136

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Expand Up @@ -5,3 +5,4 @@ gemspec

gem "nokogiri", RUBY_VERSION < "2.1" ? "~> 1.6.0" : ">= 1.7"
gem "activesupport", RUBY_VERSION < "2.2.2" ? "~> 4.2.0" : ">= 5"
gem "loofah", git: "https://github.com/flavorjones/loofah", branch: "flavorjones-scrub-accepts-allowed-attribute-names"
46 changes: 1 addition & 45 deletions lib/rails/html/scrubbers.rb
Expand Up @@ -86,10 +86,6 @@ def skip_node?(node)
node.text?
end

def scrub_attribute?(name)
!@attributes.include?(name)
end

def keep_node?(node)
if @tags
allowed_node?(node)
Expand All @@ -105,58 +101,18 @@ def scrub_node(node)

def scrub_attributes(node)
if @attributes
node.attribute_nodes.each do |attr|
attr.remove if scrub_attribute?(attr.name)
scrub_attribute(node, attr)
end

scrub_css_attribute(node)
Loofah::HTML5::Scrub.scrub_attributes(node, allowed_attribute_names: @attributes)
else
Loofah::HTML5::Scrub.scrub_attributes(node)
end
end

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

def validate!(var, name)
if var && !var.is_a?(Enumerable)
raise ArgumentError, "You should pass :#{name} as an Enumerable"
end
var
end

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

if Loofah::HTML5::SafeList::ATTR_VAL_IS_URI.include?(attr_name)
# this block lifted nearly verbatim from HTML5 sanitization
val_unescaped = CGI.unescapeHTML(attr_node.value).gsub(Loofah::HTML5::Scrub::CONTROL_CHARACTERS,'').downcase
if val_unescaped =~ /^[a-z0-9][-+.a-z0-9]*:/ && ! Loofah::HTML5::SafeList::ALLOWED_PROTOCOLS.include?(val_unescaped.split(Loofah::HTML5::SafeList::PROTOCOL_SEPARATOR)[0])
attr_node.remove
end
end
if Loofah::HTML5::SafeList::SVG_ATTR_VAL_ALLOWS_REF.include?(attr_name)
attr_node.value = attr_node.value.gsub(/url\s*\(\s*[^#\s][^)]+?\)/m, ' ') if attr_node.value
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
end

# === Rails::Html::TargetScrubber
Expand Down
10 changes: 10 additions & 0 deletions test/sanitizer_test.rb
Expand Up @@ -515,6 +515,16 @@ def test_allow_data_attribute_if_requested
assert_equal %(<a data-foo="foo">foo</a>), safe_list_sanitize(text, attributes: ['data-foo'])
end

def test_sanitize_data_protocol
text = "- XSS\"><iframe src=\"data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=\">- XSS\"><iframe src=\"data:application/vnd.wap.xhtml+xml;base64,PHg6c2NyaXB0IHhtbG5zOng9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiPmFsZXJ0KGRvY3VtZW50LmRvbWFpbik8L3g6c2NyaXB0Pg==\">"

scope_allowed_tags %w(iframe) do
scope_allowed_attributes %w(src) do
assert_equal %(- XSS\"&gt;<iframe>- XSS\"&gt;<iframe></iframe></iframe>), safe_list_sanitize(text)
end
end
end

def test_uri_escaping_of_href_attr_in_a_tag_in_safe_list_sanitizer
skip if RUBY_VERSION < "2.3"

Expand Down