CVE-2015-7580: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Cross-site scripting (XSS) vulnerability in lib/rails/html/scrubbers.rb in the rails-html-sanitizer gem before 1.0.3 for Ruby on Rails 4.2.x and 5.x allows remote attackers to inject arbitrary web script or HTML via a crafted CDATA node.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 6.1
Published: 2016-02-16T02:59Z
Repository: https://github.com/rails/rails-html-sanitizer
Language: Ruby
Files changed: lib/rails/html/scrubbers.rb, test/sanitizer_test.rb
Commit date: 2016-01-22 15:08:16 -0800
Commit message: convert CDATA nodes to TEXT nodes to avoid XSS issues

CDATA nodes will not be html escaped.  Users shouldn't be submitting
CDATA nodes in the first place, so we should convert them to text nodes
before escaping

CVE-2015-7580

Diff: diff --git a/lib/rails/html/scrubbers.rb b/lib/rails/html/scrubbers.rb
index d6f8ce4..1e6f887 100644
--- a/lib/rails/html/scrubbers.rb
+++ b/lib/rails/html/scrubbers.rb
@@ -58,10 +58,15 @@ module Rails
       def attributes=(attributes)
         @attributes = validate!(attributes, :attributes)
       end
 
       def scrub(node)
+        if node.cdata?
+          text = node.document.create_text_node node.text
+          node.replace text
+          return CONTINUE
+        end
         return CONTINUE if skip_node?(node)
 
         unless keep_node?(node)
           return STOP if scrub_node(node) == STOP
         end
@@ -74,11 +79,11 @@ module Rails
       def allowed_node?(node)
         @tags.include?(node.name)
       end
 
       def skip_node?(node)
-        node.text? || node.cdata?
+        node.text?
       end
 
       def scrub_attribute?(name)
         !@attributes.include?(name)
       end
diff --git a/test/sanitizer_test.rb b/test/sanitizer_test.rb
index 8c1ea2c..f72b702 100644
--- a/test/sanitizer_test.rb
+++ b/test/sanitizer_test.rb
@@ -9,10 +9,20 @@ class SanitizersTest < Minitest::Test
     assert_raises NotImplementedError do
       Rails::Html::Sanitizer.new.sanitize('')
     end
   end
 
+  def test_sanitize_nested_script
+    sanitizer = Rails::Html::WhiteListSanitizer.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))
+  end
+
+  def test_sanitize_nested_script_in_style
+    sanitizer = Rails::Html::WhiteListSanitizer.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))
+  end
+
   class XpathRemovalTestSanitizer < Rails::Html::Sanitizer
     def sanitize(html, options = {})
       fragment = Loofah.fragment(html)
       remove_xpaths(fragment, options[:xpaths]).to_s
     end

Vulnerable code:         node.text? || node.cdata?
Fixed code:         if node.cdata?
          text = node.document.create_text_node node.text
          node.replace text
          return CONTINUE
        end
        node.text?
  def test_sanitize_nested_script
    sanitizer = Rails::Html::WhiteListSanitizer.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))
  end

  def test_sanitize_nested_script_in_style
    sanitizer = Rails::Html::WhiteListSanitizer.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))
  end