CVE-2020-5267: Improper Neutralization of Script-Related HTML Tags in a Web Page (Basic XSS) (CWE-80)
value: In ActionView before versions 6.0.2.2 and 5.2.4.2, there is a possible XSS vulnerability in ActionView's JavaScript literal escape helpers. Views that use the `j` or `escape_javascript` methods may be susceptible to XSS attacks. The issue is fixed in versions 6.0.2.2 and 5.2.4.2.
CWE description: The product receives input from an upstream component, but it does not neutralize or incorrectly neutralizes special characters such as "<", ">", and "&" that could be interpreted as web-scripting elements when they are sent to a downstream component that processes web pages.
CVSS v3 base score: 4.8
Published: 2020-03-19T18:15Z
Repository: https://github.com/rails/rails
Language: Ruby
Files changed: actionview/lib/action_view/helpers/javascript_helper.rb, actionview/test/template/javascript_helper_test.rb
Commit date: 2020-03-19 09:48:08 -0700
Commit message: Fix possible XSS vector in JS escape helper

This commit escapes dollar signs and backticks to prevent JS XSS issues
when using the `j` or `javascript_escape` helper

CVE-2020-5267

Diff: diff --git a/actionview/lib/action_view/helpers/javascript_helper.rb b/actionview/lib/action_view/helpers/javascript_helper.rb
index 619ce9706b..0624833c14 100644
--- a/actionview/lib/action_view/helpers/javascript_helper.rb
+++ b/actionview/lib/action_view/helpers/javascript_helper.rb
@@ -10,11 +10,13 @@ module JavaScriptHelper
         "</"    => '<\/',
         "\r\n"  => '\n',
         "\n"    => '\n',
         "\r"    => '\n',
         '"'     => '\\"',
-        "'"     => "\\'"
+        "'"     => "\\'",
+        "`"     => "\\`",
+        "$"     => "\\$"
       }
 
       JS_ESCAPE_MAP[(+"\342\200\250").force_encoding(Encoding::UTF_8).encode!] = "&#x2028;"
       JS_ESCAPE_MAP[(+"\342\200\251").force_encoding(Encoding::UTF_8).encode!] = "&#x2029;"
 
@@ -27,11 +29,11 @@ module JavaScriptHelper
       def escape_javascript(javascript)
         javascript = javascript.to_s
         if javascript.empty?
           result = ""
         else
-          result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u, JS_ESCAPE_MAP)
+          result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"']|[`]|[$])/u, JS_ESCAPE_MAP)
         end
         javascript.html_safe? ? result.html_safe : result
       end
 
       alias_method :j, :escape_javascript
diff --git a/actionview/test/template/javascript_helper_test.rb b/actionview/test/template/javascript_helper_test.rb
index f974e5ae0c..4b7284d15b 100644
--- a/actionview/test/template/javascript_helper_test.rb
+++ b/actionview/test/template/javascript_helper_test.rb
@@ -34,10 +34,18 @@ def test_escape_javascript
     assert_equal %(unicode &#x2029; newline), escape_javascript((+%(unicode \342\200\251 newline)).force_encoding(Encoding::UTF_8).encode!)
 
     assert_equal %(dont <\\/close> tags), j(%(dont </close> tags))
   end
 
+  def test_escape_backtick
+    assert_equal "\\`", escape_javascript("`")
+  end
+
+  def test_escape_dollar_sign
+    assert_equal "\\$", escape_javascript("$")
+  end
+
   def test_escape_javascript_with_safebuffer
     given = %('quoted' "double-quoted" new-line:\n </closed>)
     expect = %(\\'quoted\\' \\"double-quoted\\" new-line:\\n <\\/closed>)
     assert_equal expect, escape_javascript(given)
     assert_equal expect, escape_javascript(ActiveSupport::SafeBuffer.new(given))

Vulnerable code:         "'"     => "\\'"
          result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u, JS_ESCAPE_MAP)
Fixed code:         "'"     => "\\'",
        "`"     => "\\`",
        "$"     => "\\$"
          result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"']|[`]|[$])/u, JS_ESCAPE_MAP)
  def test_escape_backtick
    assert_equal "\\`", escape_javascript("`")
  end

  def test_escape_dollar_sign
    assert_equal "\\$", escape_javascript("$")
  end