VYPR
Moderate severityNVD Advisory· Published Sep 11, 2020· Updated Aug 4, 2024

XSS in Action View

CVE-2020-15169

Description

In Action View before versions 5.2.4.4 and 6.0.3.3 there is a potential Cross-Site Scripting (XSS) vulnerability in Action View's translation helpers. Views that allow the user to control the default (not found) value of the t and translate helpers could be susceptible to XSS attacks. When an HTML-unsafe string is passed as the default for a missing translation key named html or ending in _html, the default string is incorrectly marked as HTML-safe and not escaped. This is patched in versions 6.0.3.3 and 5.2.4.4. A workaround without upgrading is proposed in the source advisory.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

CVE-2020-15169 is a stored XSS vulnerability in Rails Action View translation helpers that marks user-controlled default strings as HTML-safe when the translation key ends in '_html'.

Vulnerability

Overview

CVE-2020-15169 is a Cross-Site Scripting (XSS) vulnerability in Action View, the view layer of Ruby on Rails [1]. The flaw resides in the t and translate helpers, specifically when a developer allows user input to control the default (fallback) value for a missing translation key [4]. When the missing key is named html or ends with _html, Action View incorrectly marks the user-supplied default string as HTML-safe and does not escape it [1][4]. This bypasses Rails' default auto-escaping and can lead to XSS [1].

Exploitation

Details

To exploit this vulnerability, the attacker must be able to control the default parameter of a t or translate call for a missing translation key that ends in _html [4]. For example, in code such as <%= t("welcome_html", default: untrusted_user_controlled_string) %>, the untrusted_user_controlled_string is marked as safe and rendered without sanitization [4]. The attack requires the user to be able to inject arbitrary HTML/JavaScript through that parameter, enabling XSS in the context of the victim's browser.

Impact

A successful XSS attack allows an attacker to execute arbitrary JavaScript in the victim's browser [1]. This can lead to session hijacking, credential theft, defacement, or other malicious actions within the web application. The vulnerability has a CVSS v3.1 base score of 6.1 (Medium), reflecting the requirement that the user must control the default parameter and the key must end in _html [1].

Mitigation

Rails has patched this vulnerability in versions 5.2.4.4 and 6.0.3.3 [1][4]. Users running older or unsupported versions should upgrade to a patched release. As a workaround, developers can manually escape the default string using the html_escape helper (aliased as h) before passing it to the translation helper [4]. For example: <%= t("welcome_html", default: h(untrusted_user_controlled_string)) %>.

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
actionviewRubyGems
< 5.2.4.45.2.4.4
actionviewRubyGems
>= 6.0.0.0, < 6.0.3.36.0.3.3

Affected products

22

Patches

1
e663f084460e

Merge pull request from GHSA-cfjv-5498-mph5

https://github.com/rails/railsJonathan HefnerSep 9, 2020via ghsa
2 files changed · +15 1
  • actionview/lib/action_view/helpers/translation_helper.rb+8 1 modified
    @@ -96,9 +96,13 @@ def translate(key, **options)
                 end
               end
     
    +          html_safe_options[:default] = MISSING_TRANSLATION unless html_safe_options[:default].blank?
    +
               translation = I18n.translate(fully_resolved_key, **html_safe_options.merge(raise: i18n_raise))
     
    -          if translation.respond_to?(:map)
    +          if translation.equal?(MISSING_TRANSLATION)
    +            translated_text = options[:default].first
    +          elsif translation.respond_to?(:map)
                 translated_text = translation.map { |element| element.respond_to?(:html_safe) ? element.html_safe : element }
               else
                 translated_text = translation.respond_to?(:html_safe) ? translation.html_safe : translation
    @@ -150,6 +154,9 @@ def localize(object, **options)
           alias :l :localize
     
           private
    +        MISSING_TRANSLATION = Object.new
    +        private_constant :MISSING_TRANSLATION
    +
             def scope_key_by_partial(key)
               stringified_key = key.to_s
               if stringified_key.start_with?(".")
    
  • actionview/test/template/translation_helper_test.rb+7 0 modified
    @@ -247,6 +247,13 @@ def test_translate_with_last_default_not_named_html
         assert_equal false, translation.html_safe?
       end
     
    +  def test_translate_does_not_mark_unsourced_string_default_as_html_safe
    +    untrusted_string = "<script>alert()</script>"
    +    translation = translate(:"translations.missing", default: [:"translations.missing_html", untrusted_string])
    +    assert_equal untrusted_string, translation
    +    assert_not_predicate translation, :html_safe?
    +  end
    +
       def test_translate_with_string_default
         translation = translate(:'translations.missing', default: "A Generic String")
         assert_equal "A Generic String", translation
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

10

News mentions

0

No linked articles in our index yet.