Control: tags 1008324 + patch
Control: tags 1008324 + pending

Dear maintainer,

I've prepared an NMU for ruby-rails-html-sanitizer (versioned as 1.4.3-0.1)
and uploaded it to DELAYED/15. Please feel free to tell me if I should cancel 
it.

cu
Adrian
diff -Nru ruby-rails-html-sanitizer-1.4.2/CHANGELOG.md ruby-rails-html-sanitizer-1.4.3/CHANGELOG.md
--- ruby-rails-html-sanitizer-1.4.2/CHANGELOG.md	2021-08-29 18:20:32.000000000 +0300
+++ ruby-rails-html-sanitizer-1.4.3/CHANGELOG.md	2022-06-27 20:47:54.000000000 +0300
@@ -1,3 +1,14 @@
+## 1.4.3 / 2022-06-09
+
+* Address a possible XSS vulnerability with certain configurations of Rails::Html::Sanitizer.
+
+  Prevent the combination of `select` and `style` as allowed tags in SafeListSanitizer.
+
+  Fixes CVE-2022-32209
+
+  *Mike Dalessio*
+
+
 ## 1.4.2 / 2021-08-23
 
 * Slightly improve performance.
diff -Nru ruby-rails-html-sanitizer-1.4.2/debian/changelog ruby-rails-html-sanitizer-1.4.3/debian/changelog
--- ruby-rails-html-sanitizer-1.4.2/debian/changelog	2022-01-23 21:21:08.000000000 +0200
+++ ruby-rails-html-sanitizer-1.4.3/debian/changelog	2022-10-16 02:44:52.000000000 +0300
@@ -1,3 +1,12 @@
+ruby-rails-html-sanitizer (1.4.3-0.1) unstable; urgency=low
+
+  * Non-maintainer upload.
+  * New upstream release.
+    - Fixes FTBFS with Ruby 3. (Closes: #1008324)
+    - CVE-2022-32209: Possible XSS vulnerability.
+
+ -- Adrian Bunk <b...@debian.org>  Sun, 16 Oct 2022 02:44:52 +0300
+
 ruby-rails-html-sanitizer (1.4.2-2) unstable; urgency=medium
 
   * Team upload.
diff -Nru ruby-rails-html-sanitizer-1.4.2/lib/rails/html/sanitizer/version.rb ruby-rails-html-sanitizer-1.4.3/lib/rails/html/sanitizer/version.rb
--- ruby-rails-html-sanitizer-1.4.2/lib/rails/html/sanitizer/version.rb	2021-08-29 18:20:32.000000000 +0300
+++ ruby-rails-html-sanitizer-1.4.3/lib/rails/html/sanitizer/version.rb	2022-06-27 20:47:54.000000000 +0300
@@ -1,7 +1,7 @@
 module Rails
   module Html
     class Sanitizer
-      VERSION = "1.4.2"
+      VERSION = "1.4.3"
     end
   end
 end
diff -Nru ruby-rails-html-sanitizer-1.4.2/lib/rails/html/sanitizer.rb ruby-rails-html-sanitizer-1.4.3/lib/rails/html/sanitizer.rb
--- ruby-rails-html-sanitizer-1.4.2/lib/rails/html/sanitizer.rb	2021-08-29 18:20:32.000000000 +0300
+++ ruby-rails-html-sanitizer-1.4.3/lib/rails/html/sanitizer.rb	2022-06-27 20:47:54.000000000 +0300
@@ -141,8 +141,25 @@
 
       private
 
+      def loofah_using_html5?
+        # future-proofing, see https://github.com/flavorjones/loofah/pull/239
+        Loofah.respond_to?(:html5_mode?) && Loofah.html5_mode?
+      end
+
+      def remove_safelist_tag_combinations(tags)
+        if !loofah_using_html5? && tags.include?("select") && tags.include?("style")
+          warn("WARNING: #{self.class}: removing 'style' from safelist, should not be combined with 'select'")
+          tags.delete("style")
+        end
+        tags
+      end
+
       def allowed_tags(options)
-        options[:tags] || self.class.allowed_tags
+        if options[:tags]
+          remove_safelist_tag_combinations(options[:tags])
+        else
+          self.class.allowed_tags
+        end
       end
 
       def allowed_attributes(options)
diff -Nru ruby-rails-html-sanitizer-1.4.2/rails-html-sanitizer.gemspec ruby-rails-html-sanitizer-1.4.3/rails-html-sanitizer.gemspec
--- ruby-rails-html-sanitizer-1.4.2/rails-html-sanitizer.gemspec	2021-08-29 18:20:32.000000000 +0300
+++ ruby-rails-html-sanitizer-1.4.3/rails-html-sanitizer.gemspec	2022-06-27 20:47:54.000000000 +0300
@@ -2,42 +2,36 @@
 # This file has been automatically generated by gem2tgz #
 #########################################################
 # -*- encoding: utf-8 -*-
-# stub: rails-html-sanitizer 1.4.2 ruby lib
+# stub: rails-html-sanitizer 1.4.3 ruby lib
 
 Gem::Specification.new do |s|
   s.name = "rails-html-sanitizer".freeze
-  s.version = "1.4.2"
+  s.version = "1.4.3"
 
   s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
-  s.metadata = { "bug_tracker_uri" => "https://github.com/rails/rails-html-sanitizer/issues";, "changelog_uri" => "https://github.com/rails/rails-html-sanitizer/blob/v1.4.2/CHANGELOG.md";, "documentation_uri" => "https://www.rubydoc.info/gems/rails-html-sanitizer/1.4.2";, "source_code_uri" => "https://github.com/rails/rails-html-sanitizer/tree/v1.4.2"; } if s.respond_to? :metadata=
+  s.metadata = { "bug_tracker_uri" => "https://github.com/rails/rails-html-sanitizer/issues";, "changelog_uri" => "https://github.com/rails/rails-html-sanitizer/blob/v1.4.3/CHANGELOG.md";, "documentation_uri" => "https://www.rubydoc.info/gems/rails-html-sanitizer/1.4.3";, "source_code_uri" => "https://github.com/rails/rails-html-sanitizer/tree/v1.4.3"; } if s.respond_to? :metadata=
   s.require_paths = ["lib".freeze]
   s.authors = ["Rafael Mendon\u00E7a Fran\u00E7a".freeze, "Kasper Timm Hansen".freeze]
-  s.date = "2021-08-24"
+  s.date = "2022-06-09"
   s.description = "HTML sanitization for Rails applications".freeze
   s.email = ["rafaelmfra...@gmail.com".freeze, "kas...@gmail.com".freeze]
   s.files = ["CHANGELOG.md".freeze, "MIT-LICENSE".freeze, "README.md".freeze, "lib/rails-html-sanitizer.rb".freeze, "lib/rails/html/sanitizer.rb".freeze, "lib/rails/html/sanitizer/version.rb".freeze, "lib/rails/html/scrubbers.rb".freeze, "test/sanitizer_test.rb".freeze, "test/scrubbers_test.rb".freeze]
   s.homepage = "https://github.com/rails/rails-html-sanitizer".freeze
   s.licenses = ["MIT".freeze]
-  s.rubygems_version = "2.7.6.2".freeze
+  s.rubygems_version = "3.2.5".freeze
   s.summary = "This gem is responsible to sanitize HTML fragments in Rails applications.".freeze
   s.test_files = ["test/sanitizer_test.rb".freeze, "test/scrubbers_test.rb".freeze]
 
   if s.respond_to? :specification_version then
     s.specification_version = 4
+  end
 
-    if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
-      s.add_development_dependency(%q<bundler>.freeze, [">= 1.3"])
-      s.add_runtime_dependency(%q<loofah>.freeze, ["~> 2.3"])
-      s.add_development_dependency(%q<minitest>.freeze, [">= 0"])
-      s.add_development_dependency(%q<rails-dom-testing>.freeze, [">= 0"])
-      s.add_development_dependency(%q<rake>.freeze, [">= 0"])
-    else
-      s.add_dependency(%q<bundler>.freeze, [">= 1.3"])
-      s.add_dependency(%q<loofah>.freeze, ["~> 2.3"])
-      s.add_dependency(%q<minitest>.freeze, [">= 0"])
-      s.add_dependency(%q<rails-dom-testing>.freeze, [">= 0"])
-      s.add_dependency(%q<rake>.freeze, [">= 0"])
-    end
+  if s.respond_to? :add_runtime_dependency then
+    s.add_development_dependency(%q<bundler>.freeze, [">= 1.3"])
+    s.add_runtime_dependency(%q<loofah>.freeze, ["~> 2.3"])
+    s.add_development_dependency(%q<minitest>.freeze, [">= 0"])
+    s.add_development_dependency(%q<rails-dom-testing>.freeze, [">= 0"])
+    s.add_development_dependency(%q<rake>.freeze, [">= 0"])
   else
     s.add_dependency(%q<bundler>.freeze, [">= 1.3"])
     s.add_dependency(%q<loofah>.freeze, ["~> 2.3"])
diff -Nru ruby-rails-html-sanitizer-1.4.2/test/sanitizer_test.rb ruby-rails-html-sanitizer-1.4.3/test/sanitizer_test.rb
--- ruby-rails-html-sanitizer-1.4.2/test/sanitizer_test.rb	2021-08-29 18:20:32.000000000 +0300
+++ ruby-rails-html-sanitizer-1.4.3/test/sanitizer_test.rb	2022-06-27 20:47:54.000000000 +0300
@@ -2,6 +2,8 @@
 require "rails-html-sanitizer"
 require "rails/dom/testing/assertions/dom_assertions"
 
+puts Nokogiri::VERSION_INFO
+
 class SanitizersTest < Minitest::Test
   include Rails::Dom::Testing::Assertions::DomAssertions
 
@@ -12,13 +14,11 @@
   end
 
   def test_sanitize_nested_script
-    sanitizer = Rails::Html::SafeListSanitizer.new
-    assert_equal '&lt;script&gt;alert("XSS");&lt;/script&gt;', sanitizer.sanitize('<script><script></script>alert("XSS");<script><</script>/</script><script>script></script>', tags: %w(em))
+    assert_equal '&lt;script&gt;alert("XSS");&lt;/script&gt;', safe_list_sanitize('<script><script></script>alert("XSS");<script><</script>/</script><script>script></script>', tags: %w(em))
   end
 
   def test_sanitize_nested_script_in_style
-    sanitizer = Rails::Html::SafeListSanitizer.new
-    assert_equal '&lt;script&gt;alert("XSS");&lt;/script&gt;', sanitizer.sanitize('<style><script></style>alert("XSS");<style><</style>/</style><style>script></style>', tags: %w(em))
+    assert_equal '&lt;script&gt;alert("XSS");&lt;/script&gt;', safe_list_sanitize('<style><script></style>alert("XSS");<style><</style>/</style><style>script></style>', tags: %w(em))
   end
 
   class XpathRemovalTestSanitizer < Rails::Html::Sanitizer
@@ -54,7 +54,8 @@
 
   def test_strip_tags_with_quote
     input = '<" <img src="trollface.gif" onload="alert(1)"> hi'
-    assert_equal ' hi', full_sanitize(input)
+    expected = libxml_2_9_14_recovery? ? %{&lt;"  hi} : %{ hi}
+    assert_equal(expected, full_sanitize(input))
   end
 
   def test_strip_invalid_html
@@ -75,15 +76,21 @@
   end
 
   def test_remove_unclosed_tags
-    assert_equal "This is ", full_sanitize("This is <-- not\n a comment here.")
+    input = "This is <-- not\n a comment here."
+    expected = libxml_2_9_14_recovery? ? %{This is &lt;-- not\n a comment here.} : %{This is }
+    assert_equal(expected, full_sanitize(input))
   end
 
   def test_strip_cdata
-    assert_equal "This has a ]]&gt; here.", full_sanitize("This has a <![CDATA[<section>]]> here.")
+    input = "This has a <![CDATA[<section>]]> here."
+    expected = libxml_2_9_14_recovery? ? %{This has a &lt;![CDATA[]]&gt; here.} : %{This has a ]]&gt; here.}
+    assert_equal(expected, full_sanitize(input))
   end
 
   def test_strip_unclosed_cdata
-    assert_equal "This has an unclosed ]] here...", full_sanitize("This has an unclosed <![CDATA[<section>]] here...")
+    input = "This has an unclosed <![CDATA[<section>]] here..."
+    expected = libxml_2_9_14_recovery? ? %{This has an unclosed &lt;![CDATA[]] here...} : %{This has an unclosed ]] here...}
+    assert_equal(expected, full_sanitize(input))
   end
 
   def test_strip_blank_string
@@ -414,8 +421,25 @@
   end
 
   def test_should_sanitize_div_background_image_unicode_encoded
-    raw = %(background-image:\u0075\u0072\u006C\u0028\u0027\u006a\u0061\u0076\u0061\u0073\u0063\u0072\u0069\u0070\u0074\u003a\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0032\u0033\u0034\u0029\u0027\u0029)
-    assert_equal '', sanitize_css(raw)
+    [
+      convert_to_css_hex("url(javascript:alert(1))", false),
+      convert_to_css_hex("url(javascript:alert(1))", true),
+      convert_to_css_hex("url(https://example.com)", false),
+      convert_to_css_hex("url(https://example.com)", true),
+    ].each do |propval|
+      raw = "background-image:" + propval
+      assert_empty(sanitize_css(raw))
+    end
+  end
+
+  def test_should_allow_div_background_image_unicode_encoded_safe_functions
+    [
+      convert_to_css_hex("rgb(255,0,0)", false),
+      convert_to_css_hex("rgb(255,0,0)", true),
+    ].each do |propval|
+      raw = "background-image:" + propval
+      assert_includes(sanitize_css(raw), "background-image")
+    end
   end
 
   def test_should_sanitize_div_style_expression
@@ -433,11 +457,15 @@
   end
 
   def test_should_sanitize_cdata_section
-    assert_sanitized "<![CDATA[<span>section</span>]]>", "section]]&gt;"
+    input = "<![CDATA[<span>section</span>]]>"
+    expected = libxml_2_9_14_recovery? ? %{&lt;![CDATA[<span>section</span>]]&gt;} : %{section]]&gt;}
+    assert_sanitized(input, expected)
   end
 
   def test_should_sanitize_unterminated_cdata_section
-    assert_sanitized "<![CDATA[<span>neverending...", "neverending..."
+    input = "<![CDATA[<span>neverending..."
+    expected = libxml_2_9_14_recovery? ? %{&lt;![CDATA[<span>neverending...</span>} : %{neverending...}
+    assert_sanitized(input, expected)
   end
 
   def test_should_not_mangle_urls_with_ampersand
@@ -488,7 +516,13 @@
 
     text = safe_list_sanitize(html)
 
-    assert_equal %{<a href=\"examp&lt;!--%22%20unsafeattr=foo()&gt;--&gt;le.com\">test</a>}, text
+    acceptable_results = [
+      # nokogiri w/vendored+patched libxml2
+      %{<a href="examp&lt;!--%22%20unsafeattr=foo()&gt;--&gt;le.com">test</a>},
+      # nokogiri w/ system libxml2
+      %{<a href="examp<!--%22%20unsafeattr=foo()>-->le.com">test</a>},
+    ]
+    assert_includes(acceptable_results, text)
   end
 
   def test_uri_escaping_of_src_attr_in_a_tag_in_safe_list_sanitizer
@@ -498,7 +532,13 @@
 
     text = safe_list_sanitize(html)
 
-    assert_equal %{<a src=\"examp&lt;!--%22%20unsafeattr=foo()&gt;--&gt;le.com\">test</a>}, text
+    acceptable_results = [
+      # nokogiri w/vendored+patched libxml2
+      %{<a src="examp&lt;!--%22%20unsafeattr=foo()&gt;--&gt;le.com">test</a>},
+      # nokogiri w/system libxml2
+      %{<a src="examp<!--%22%20unsafeattr=foo()>-->le.com">test</a>},
+    ]
+    assert_includes(acceptable_results, text)
   end
 
   def test_uri_escaping_of_name_attr_in_a_tag_in_safe_list_sanitizer
@@ -508,7 +548,13 @@
 
     text = safe_list_sanitize(html)
 
-    assert_equal %{<a name=\"examp&lt;!--%22%20unsafeattr=foo()&gt;--&gt;le.com\">test</a>}, text
+    acceptable_results = [
+      # nokogiri w/vendored+patched libxml2
+      %{<a name="examp&lt;!--%22%20unsafeattr=foo()&gt;--&gt;le.com">test</a>},
+      # nokogiri w/system libxml2
+      %{<a name="examp<!--%22%20unsafeattr=foo()>-->le.com">test</a>},
+    ]
+    assert_includes(acceptable_results, text)
   end
 
   def test_uri_escaping_of_name_action_in_a_tag_in_safe_list_sanitizer
@@ -518,7 +564,13 @@
 
     text = safe_list_sanitize(html, attributes: ['action'])
 
-    assert_equal %{<a action=\"examp&lt;!--%22%20unsafeattr=foo()&gt;--&gt;le.com\">test</a>}, text
+    acceptable_results = [
+      # nokogiri w/vendored+patched libxml2
+      %{<a action="examp&lt;!--%22%20unsafeattr=foo()&gt;--&gt;le.com">test</a>},
+      # nokogiri w/system libxml2
+      %{<a action="examp<!--%22%20unsafeattr=foo()>-->le.com">test</a>},
+    ]
+    assert_includes(acceptable_results, text)
   end
 
   def test_exclude_node_type_processing_instructions
@@ -529,6 +581,25 @@
     assert_equal("<div>text</div><b>text</b>", safe_list_sanitize("<div>text</div><!-- comment --><b>text</b>"))
   end
 
+  def test_disallow_the_dangerous_safelist_combination_of_select_and_style
+    input = "<select><style><script>alert(1)</script></style></select>"
+    tags = ["select", "style"]
+    warning = /WARNING: Rails::Html::SafeListSanitizer: removing 'style' from safelist/
+    sanitized = nil
+    invocation = Proc.new { sanitized = safe_list_sanitize(input, tags: tags) }
+
+    if html5_mode?
+      # if Loofah is using an HTML5 parser,
+      #   then "style" should be removed by the parser as an invalid child of "select"
+      assert_silent(&invocation)
+    else
+      # if Loofah is using an HTML4 parser,
+      #   then SafeListSanitizer should remove "style" from the safelist
+      assert_output(nil, warning, &invocation)
+    end
+    refute_includes(sanitized, "style")
+  end
+
 protected
 
   def xpath_sanitize(input, options = {})
@@ -574,4 +645,23 @@
   ensure
     Rails::Html::SafeListSanitizer.allowed_attributes = old_attributes
   end
+
+  # note that this is used for testing CSS hex encoding: \\[0-9a-f]{1,6}
+  def convert_to_css_hex(string, escape_parens=false)
+    string.chars.map do |c|
+      if !escape_parens && (c == "(" || c == ")")
+        c
+      else
+        format('\00%02X', c.ord)
+      end
+    end.join
+  end
+
+  def libxml_2_9_14_recovery?
+    Nokogiri.method(:uses_libxml?).arity == -1 && Nokogiri.uses_libxml?(">= 2.9.14")
+  end
+
+  def html5_mode?
+    ::Loofah.respond_to?(:html5_mode?) && ::Loofah.html5_mode?
+  end
 end

Reply via email to