VYPR
Moderate severityNVD Advisory· Published Oct 19, 2021· Updated Aug 6, 2024

CVE-2011-1497

CVE-2011-1497

Description

A cross-site scripting vulnerability flaw was found in the auto_link function in Rails before version 3.0.6.

AI Insight

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

Cross-site scripting vulnerability in the auto_link function in Ruby on Rails before 3.0.6 allowed attackers to inject arbitrary JavaScript via unsanitized user input.

Vulnerability

A cross-site scripting (XSS) vulnerability exists in the auto_link helper function provided by Action Pack in Ruby on Rails versions prior to 3.0.6. The function did not sanitize user-supplied input and incorrectly marked the output as HTML-safe, allowing injection of arbitrary HTML and JavaScript [1] [4].

Exploitation

An attacker can exploit this vulnerability by providing crafted input containing malicious HTML or JavaScript (e.g., `) that is later processed by auto_link. The function would generate a link but preserve the injected script, which then executes in the context of any user viewing the page. No authentication or special network position is required; the attacker only needs to submit the malicious input to a field or parameter that is later displayed using auto_link` [4].

Impact

Successful exploitation allows an attacker to execute arbitrary JavaScript in the victim's browser, leading to session hijacking, cookie theft, defacement, or phishing attacks. The attack runs with the same permissions as the victim user and can affect any page that renders user-controlled data through the vulnerable auto_link call [4].

Mitigation

The vulnerability is fixed in Ruby on Rails version 3.0.6, released on April 5, 2011 [4]. Users should upgrade to at least this version. As a workaround before upgrading, developers must wrap auto_link() calls with sanitize() to ensure output is properly escaped. For example: <%= sanitize(auto_link(some_user_input)) %>. If the input is known to be safe, raw() can be used, but caution is advised [4].

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
actionpackRubyGems
>= 3.0.0.rc, < 3.0.63.0.6

Affected products

2

Patches

2
61ee3449674c

do not return html safe strings from auto_link

https://github.com/rails/railsAaron PattersonApr 5, 2011via ghsa
2 files changed · +13 7
  • actionpack/lib/action_view/helpers/text_helper.rb+2 2 modified
    @@ -299,7 +299,7 @@ def simple_format(text, html_options={}, options={})
           #   # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.myblog.com</a>.
           #         Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
           def auto_link(text, *args, &block)#link = :all, html = {}, &block)
    -        return ''.html_safe if text.blank?
    +        return '' if text.blank?
     
             options = args.size == 2 ? {} : args.extract_options! # this is necessary because the old auto_link API has a Hash as its last parameter
             unless args.empty?
    @@ -503,7 +503,7 @@ def auto_link_urls(text, html_options = {}, options = {})
                   end
                   content_tag(:a, link_text, link_attributes.merge('href' => href), !!options[:sanitize]) + punctuation.reverse.join('')
                 end
    -          end.html_safe
    +          end
             end
     
             # Turns all email addresses into clickable links.  If a block is given,
    
  • actionpack/test/template/text_helper_test.rb+11 5 modified
    @@ -315,14 +315,20 @@ def generate_result(link_text, href = nil, escape = false)
         end
       end
     
    -  def test_auto_link_should_be_html_safe
    +  def test_auto_link_should_not_be_html_safe
         email_raw    = 'santiago@wyeworks.com'
         link_raw     = 'http://www.rubyonrails.org'
     
    -    assert auto_link(nil).html_safe?
    -    assert auto_link('').html_safe?
    -    assert auto_link("#{link_raw} #{link_raw} #{link_raw}").html_safe?
    -    assert auto_link("hello #{email_raw}").html_safe?
    +    assert !auto_link(nil).html_safe?, 'should not be html safe'
    +    assert !auto_link('').html_safe?, 'should not be html safe'
    +    assert !auto_link("#{link_raw} #{link_raw} #{link_raw}").html_safe?, 'should not be html safe'
    +    assert !auto_link("hello #{email_raw}").html_safe?, 'should not be html safe'
    +  end
    +
    +  def test_auto_link_email_address
    +    email_raw    = 'aaron@tenderlovemaking.com'
    +    email_result = %{<a href="mailto:#{email_raw}">#{email_raw}</a>}
    +    assert !auto_link_email_addresses(email_result).html_safe?, 'should not be html safe'
       end
     
       def test_auto_link
    
ab764ecbfea3

Makes text_helper methods sanitize the input if the input is not safe or :safe => true option is not provided

https://github.com/rails/railsSantiago PastorinoJun 6, 2010via ghsa
2 files changed · +118 22
  • actionpack/lib/action_view/helpers/text_helper.rb+22 16 modified
    @@ -74,6 +74,7 @@ def truncate(text, *args)
     
             options.reverse_merge!(:length => 30)
     
    +        text = sanitize(text) unless text.html_safe? || options[:safe]
             text.truncate(options.delete(:length), options) if text
           end
     
    @@ -105,6 +106,7 @@ def highlight(text, phrases, *args)
             end
             options.reverse_merge!(:highlighter => '<strong class="highlight">\1</strong>')
     
    +        text = sanitize(text) unless text.html_safe? || options[:safe]
             if text.blank? || phrases.blank?
               text
             else
    @@ -244,13 +246,14 @@ def word_wrap(text, *args)
           #
           def textilize(text, *options)
             options ||= [:hard_breaks]
    +        text = sanitize(text) unless text.html_safe? || options.delete(:safe)
     
             if text.blank?
               ""
             else
               textilized = RedCloth.new(text, options)
               textilized.to_html
    -        end
    +        end.html_safe
           end
     
           # Returns the text with all the Textile codes turned into HTML tags,
    @@ -271,8 +274,8 @@ def textilize(text, *options)
           #
           #   textilize_without_paragraph("Visit the Rails website "here":http://www.rubyonrails.org/.)
           #   # => "Visit the Rails website <a href="http://www.rubyonrails.org/">here</a>."
    -      def textilize_without_paragraph(text)
    -        textiled = textilize(text)
    +      def textilize_without_paragraph(text, *options)
    +        textiled = textilize(text, options)
             if textiled[0..2] == "<p>" then textiled = textiled[3..-1] end
             if textiled[-4..-1] == "</p>" then textiled = textiled[0..-5] end
             return textiled
    @@ -295,8 +298,9 @@ def textilize_without_paragraph(text)
           #
           #   markdown('![The ROR logo](http://rubyonrails.com/images/rails.png "Ruby on Rails")')
           #   # => '<p><img src="http://rubyonrails.com/images/rails.png" alt="The ROR logo" title="Ruby on Rails" /></p>'
    -      def markdown(text)
    -        text.blank? ? "" : BlueCloth.new(text).to_html
    +      def markdown(text, options = {})
    +        text = sanitize(text) unless options[:safe]
    +        (text.blank? ? "" : BlueCloth.new(text).to_html).html_safe
           end
     
           # Returns +text+ transformed into HTML using simple formatting rules.
    @@ -320,14 +324,15 @@ def markdown(text)
           #
           #   simple_format("Look ma! A class!", :class => 'description')
           #   # => "<p class='description'>Look ma! A class!</p>"
    -      def simple_format(text, html_options={})
    +      def simple_format(text, html_options={}, options={})
    +        text = '' if text.nil?
             start_tag = tag('p', html_options, true)
    -        text = h(text)
    +        text = sanitize(text) unless text.html_safe? || options[:safe]
             text.gsub!(/\r\n?/, "\n")                    # \r\n and \r -> \n
             text.gsub!(/\n\n+/, "</p>\n\n#{start_tag}")  # 2+ newline  -> paragraph
             text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline   -> br
             text.insert 0, start_tag
    -        text.safe_concat("</p>")
    +        text.html_safe.safe_concat("</p>")
           end
     
           # Turns all URLs and e-mail addresses into clickable links. The <tt>:link</tt> option
    @@ -368,7 +373,7 @@ def simple_format(text, html_options={})
           #   # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.myblog.com</a>.
           #         Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
           def auto_link(text, *args, &block)#link = :all, html = {}, &block)
    -        return '' if text.blank?
    +        return ''.html_safe if text.blank?
     
             options = args.size == 2 ? {} : args.extract_options! # this is necessary because the old auto_link API has a Hash as its last parameter
             unless args.empty?
    @@ -378,9 +383,9 @@ def auto_link(text, *args, &block)#link = :all, html = {}, &block)
             options.reverse_merge!(:link => :all, :html => {})
     
             case options[:link].to_sym
    -          when :all                         then auto_link_email_addresses(auto_link_urls(text, options[:html], &block), options[:html], &block)
    +          when :all                         then auto_link_email_addresses(auto_link_urls(text, options[:html], options, &block), options[:html], &block)
               when :email_addresses             then auto_link_email_addresses(text, options[:html], &block)
    -          when :urls                        then auto_link_urls(text, options[:html], &block)
    +          when :urls                        then auto_link_urls(text, options[:html], options, &block)
             end
           end
     
    @@ -544,7 +549,7 @@ def set_cycle(name, cycle_object)
     
             # Turns all urls into clickable links.  If a block is given, each url
             # is yielded and the result is used as the link text.
    -        def auto_link_urls(text, html_options = {})
    +        def auto_link_urls(text, html_options = {}, options = {})
               link_attributes = html_options.stringify_keys
               text.gsub(AUTO_LINK_RE) do
                 scheme, href = $1, $&
    @@ -566,21 +571,22 @@ def auto_link_urls(text, html_options = {})
                   link_text = block_given?? yield(href) : href
                   href = 'http://' + href unless scheme
     
    -              content_tag(:a, link_text, link_attributes.merge('href' => href)) + punctuation.reverse.join('')
    +              content_tag(:a, link_text, link_attributes.merge('href' => href), !(options[:safe] || text.html_safe?)) + punctuation.reverse.join('')
                 end
    -          end
    +          end.html_safe
             end
     
             # Turns all email addresses into clickable links.  If a block is given,
             # each email is yielded and the result is used as the link text.
    -        def auto_link_email_addresses(text, html_options = {})
    +        def auto_link_email_addresses(text, html_options = {}, options = {})
               text.gsub(AUTO_EMAIL_RE) do
                 text = $&
     
                 if auto_linked?($`, $')
    -              text
    +              text.html_safe
                 else
                   display_text = (block_given?) ? yield(text) : text
    +              display_text = sanitize(display_text) unless options[:safe]
                   mail_to text, display_text, html_options
                 end
               end
    
  • actionpack/test/template/text_helper_test.rb+96 6 modified
    @@ -45,19 +45,42 @@ def test_simple_format_should_be_html_safe
         assert simple_format("<b> test with html tags </b>").html_safe?
       end
     
    -  def test_simple_format_should_escape_unsafe_input
    -    assert_equal "<p>&lt;b&gt; test with unsafe string &lt;/b&gt;</p>", simple_format("<b> test with unsafe string </b>")
    +  def test_simple_format_should_sanitize_unsafe_input
    +    assert_equal "<p><b> test with unsafe string </b></p>", simple_format("<b> test with unsafe string </b><script>code!</script>")
       end
     
    -  def test_simple_format_should_not_escape_safe_input
    +  def test_simple_format_should_not_sanitize_input_if_safe_option
    +    assert_equal "<p><b> test with unsafe string </b><script>code!</script></p>", simple_format("<b> test with unsafe string </b><script>code!</script>", {}, :safe => true)
    +  end
    +
    +  def test_simple_format_should_not_sanitize_safe_input
         assert_equal "<p><b> test with safe string </b></p>", simple_format("<b> test with safe string </b>".html_safe)
       end
     
    +  def test_truncate_should_be_html_safe
    +    assert truncate("Hello World!", :length => 12).html_safe?
    +  end
    +  
       def test_truncate
         assert_equal "Hello World!", truncate("Hello World!", :length => 12)
         assert_equal "Hello Wor...", truncate("Hello World!!", :length => 12)
       end
     
    +  def test_truncate_should_sanitize_unsafe_input
    +    assert_equal "Hello World!", truncate("Hello <script>code!</script>World!", :length => 12)
    +    assert_equal "Hello Wor...", truncate("Hello <script>code!</script>World!!", :length => 12)
    +  end
    +
    +  def test_truncate_should_not_sanitize_input_if_safe_option
    +    assert_equal "Hello <sc...", truncate("Hello <script>code!</script>World!", :length => 12, :safe => true)
    +    assert_equal "Hello <sc...", truncate("Hello <script>code!</script>World!!", :length => 12, :safe => true)
    +  end
    +
    +  def test_truncate_should_not_sanitize_safe_input
    +    assert_equal "Hello <sc...", truncate("Hello <script>code!</script>World!".html_safe, :length => 12)
    +    assert_equal "Hello <sc...", truncate("Hello <script>code!</script>World!!".html_safe, :length => 12)
    +  end
    +
       def test_truncate_should_use_default_length_of_30
         str = "This is a string that will go longer then the default truncate length of 30"
         assert_equal str[0...27] + "...", truncate(str)
    @@ -93,7 +116,11 @@ def test_truncate_multibyte
         end
       end
     
    -  def test_highlighter
    +  def test_highlight_should_be_html_safe
    +    assert highlight("This is a beautiful morning", "beautiful").html_safe?
    +  end
    + 
    +  def test_highlight
         assert_equal(
           "This is a <strong class=\"highlight\">beautiful</strong> morning",
           highlight("This is a beautiful morning", "beautiful")
    @@ -117,6 +144,27 @@ def test_highlighter
         assert_equal '   ', highlight('   ', 'blank text is returned verbatim')
       end
     
    +  def test_highlight_should_sanitize_unsafe_input
    +    assert_equal(
    +      "This is a <strong class=\"highlight\">beautiful</strong> morning",
    +      highlight("This is a beautiful morning<script>code!</script>", "beautiful")
    +    )
    +  end
    +
    +  def test_highlight_should_not_sanitize_input_if_safe_option
    +    assert_equal(
    +      "This is a <strong class=\"highlight\">beautiful</strong> morning<script>code!</script>",
    +      highlight("This is a beautiful morning<script>code!</script>", "beautiful", :safe => true)
    +    )
    +  end
    +
    +  def test_highlight_should_not_sanitize_safe_input
    +    assert_equal(
    +      "This is a <strong class=\"highlight\">beautiful</strong> morning<script>code!</script>",
    +      highlight("This is a beautiful morning<script>code!</script>".html_safe, "beautiful")
    +    )
    +  end
    +
       def test_highlight_with_regexp
         assert_equal(
           "This is a <strong class=\"highlight\">beautiful!</strong> morning",
    @@ -163,7 +211,7 @@ def test_highlight_with_html
           highlight("<p class=\"beautiful\">This is a beautiful morning, but also a beautiful day</p>", "beautiful")
         )
         assert_equal(
    -      "<p>This is a <strong class=\"highlight\">beautiful</strong> <a href=\"http://example.com/beautiful\#top?what=beautiful%20morning&when=now+then\">morning</a>, but also a <strong class=\"highlight\">beautiful</strong> day</p>",
    +      "<p>This is a <strong class=\"highlight\">beautiful</strong> <a href=\"http://example.com/beautiful\#top?what=beautiful%20morning&amp;when=now+then\">morning</a>, but also a <strong class=\"highlight\">beautiful</strong> day</p>",
           highlight("<p>This is a beautiful <a href=\"http://example.com/beautiful\#top?what=beautiful%20morning&when=now+then\">morning</a>, but also a beautiful day</p>", "beautiful")
         )
       end
    @@ -286,7 +334,17 @@ def generate_result(link_text, href = nil)
         %{<a href="#{CGI::escapeHTML href}">#{CGI::escapeHTML link_text}</a>}
       end
     
    -  def test_auto_linking
    +  def test_auto_link_should_be_html_safe
    +    email_raw    = 'santiago@wyeworks.com'
    +    link_raw     = 'http://www.rubyonrails.org'
    +
    +    assert auto_link(nil).html_safe?
    +    assert auto_link('').html_safe?
    +    assert auto_link("#{link_raw} #{link_raw} #{link_raw}").html_safe?
    +    assert auto_link("hello #{email_raw}").html_safe?
    +  end
    +
    +  def test_auto_link
         email_raw    = 'david@loudthinking.com'
         email_result = %{<a href="mailto:#{email_raw}">#{email_raw}</a>}
         link_raw     = 'http://www.rubyonrails.com'
    @@ -378,6 +436,21 @@ def test_auto_linking
         assert_equal %(<p>#{link10_result} Link</p>), auto_link("<p>#{link10_raw} Link</p>")
       end
     
    +  def test_auto_link_should_sanitize_unsafe_input
    +    link_raw     = %{http://www.rubyonrails.com?id=1&num=2}
    +    assert_equal %{<a href="http://www.rubyonrails.com?id=1&amp;num=2">http://www.rubyonrails.com?id=1&amp;num=2</a>}, auto_link(link_raw)
    +  end
    +
    +  def test_auto_link_should_sanitize_unsafe_input
    +    link_raw     = %{http://www.rubyonrails.com?id=1&num=2}
    +    assert_equal %{<a href="http://www.rubyonrails.com?id=1&num=2">http://www.rubyonrails.com?id=1&num=2</a>}, auto_link(link_raw, :safe => true)
    +  end
    +
    +  def test_auto_link_should_not_sanitize_safe_input
    +    link_raw     = %{http://www.rubyonrails.com?id=1&num=2}
    +    assert_equal %{<a href="http://www.rubyonrails.com?id=1&num=2">http://www.rubyonrails.com?id=1&num=2</a>}, auto_link(link_raw.html_safe)
    +  end
    +
       def test_auto_link_other_protocols
         ftp_raw = 'ftp://example.com/file.txt'
         assert_equal %(Download #{generate_result(ftp_raw)}), auto_link("Download #{ftp_raw}")
    @@ -587,7 +660,12 @@ def test_cycle_no_instance_variable_clashes
         assert_equal(%w{Specialized Fuji Giant}, @cycles)
       end
     
    +  # TODO test textilize_without_paragraph and markdown
       if defined? RedCloth
    +    def test_textilize_should_be_html_safe
    +      assert textilize("*This is Textile!*  Rejoice!").html_safe?
    +    end
    +
         def test_textilize
           assert_equal("<p><strong>This is Textile!</strong>  Rejoice!</p>", textilize("*This is Textile!*  Rejoice!"))
         end
    @@ -600,6 +678,18 @@ def test_textilize_with_options
           assert_equal("<p>This is worded &lt;strong&gt;strongly&lt;/strong&gt;</p>", textilize("This is worded <strong>strongly</strong>", :filter_html))
         end
     
    +    def test_textilize_should_sanitize_unsafe_input
    +      assert_equal("<p>This is worded <strong>strongly</strong></p>", textilize("This is worded <strong>strongly</strong><script>code!</script>"))
    +    end
    +
    +    def test_textilize_should_not_sanitize_input_if_safe_option
    +      assert_equal("<p>This is worded <strong>strongly</strong><script>code!</script></p>", textilize("This is worded <strong>strongly</strong><script>code!</script>", :safe))
    +    end
    +
    +    def test_textilize_should_not_sanitize_safe_input
    +      assert_equal("<p>This is worded <strong>strongly</strong><script>code!</script></p>", textilize("This is worded <strong>strongly</strong><script>code!</script>".html_safe))
    +    end
    +
         def test_textilize_with_hard_breaks
           assert_equal("<p>This is one scary world.<br />\n True.</p>", textilize("This is one scary world.\n True."))
         end
    

Vulnerability mechanics

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

References

7

News mentions

0

No linked articles in our index yet.