diff options
-rw-r--r-- | actionpack/lib/action_view/helpers/text_helper.rb | 38 | ||||
-rw-r--r-- | actionpack/test/template/text_helper_test.rb | 102 |
2 files changed, 118 insertions, 22 deletions
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index bfad9f8d31..4c76e9642f 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -74,6 +74,7 @@ module ActionView 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 @@ module ActionView 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 @@ module ActionView # 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 @@ module ActionView # # 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 @@ module ActionView # # markdown('') # # => '<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 @@ module ActionView # # 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 @@ module ActionView # # => "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 @@ module ActionView 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 @@ module ActionView # 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 @@ module ActionView 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 diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index bb808b77a5..9d7106b2e5 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -45,19 +45,42 @@ class TextHelperTest < ActionView::TestCase assert simple_format("<b> test with html tags </b>").html_safe? end - def test_simple_format_should_escape_unsafe_input - assert_equal "<p><b> test with unsafe string </b></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 @@ class TextHelperTest < ActionView::TestCase 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 @@ class TextHelperTest < ActionView::TestCase 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 @@ class TextHelperTest < ActionView::TestCase 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&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 @@ class TextHelperTest < ActionView::TestCase %{<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 @@ class TextHelperTest < ActionView::TestCase 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&num=2">http://www.rubyonrails.com?id=1&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 @@ class TextHelperTest < ActionView::TestCase 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 @@ class TextHelperTest < ActionView::TestCase assert_equal("<p>This is worded <strong>strongly</strong></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 |