From ab764ecbfea31a3b14323283287e2fc80955ace6 Mon Sep 17 00:00:00 2001
From: Santiago Pastorino
Date: Sun, 6 Jun 2010 02:16:26 -0300
Subject: Makes text_helper methods sanitize the input if the input is not safe
or :safe => true option is not provided
---
actionpack/lib/action_view/helpers/text_helper.rb | 38 ++++----
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 => '\1')
+ 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 here."
- def textilize_without_paragraph(text)
- textiled = textilize(text)
+ def textilize_without_paragraph(text, *options)
+ textiled = textilize(text, options)
if textiled[0..2] == "" then textiled = textiled[3..-1] end
if textiled[-4..-1] == "
" then textiled = textiled[0..-5] end
return textiled
@@ -295,8 +298,9 @@ module ActionView
#
# markdown('![The ROR logo](http://rubyonrails.com/images/rails.png "Ruby on Rails")')
# # => ''
- 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')
# # => "Look ma! A class!
"
- 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+/, "
\n\n#{start_tag}") # 2+ newline -> paragraph
text.gsub!(/([^\n]\n)(?=[^\n])/, '\1
') # 1 newline -> br
text.insert 0, start_tag
- text.safe_concat("")
+ text.html_safe.safe_concat("")
end
# Turns all URLs and e-mail addresses into clickable links. The :link option
@@ -368,7 +373,7 @@ module ActionView
# # => "Welcome to my new blog at http://www.myblog.com.
# Please e-mail me at me@email.com."
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(" test with html tags ").html_safe?
end
- def test_simple_format_should_escape_unsafe_input
- assert_equal "<b> test with unsafe string </b>
", simple_format(" test with unsafe string ")
+ def test_simple_format_should_sanitize_unsafe_input
+ assert_equal " test with unsafe string
", simple_format(" test with unsafe string ")
end
- def test_simple_format_should_not_escape_safe_input
+ def test_simple_format_should_not_sanitize_input_if_safe_option
+ assert_equal " test with unsafe string
", simple_format(" test with unsafe string ", {}, :safe => true)
+ end
+
+ def test_simple_format_should_not_sanitize_safe_input
assert_equal " test with safe string
", simple_format(" test with safe string ".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 World!", :length => 12)
+ assert_equal "Hello Wor...", truncate("Hello World!!", :length => 12)
+ end
+
+ def test_truncate_should_not_sanitize_input_if_safe_option
+ assert_equal "Hello code!World!", :length => 12, :safe => true)
+ assert_equal "Hello code!World!!", :length => 12, :safe => true)
+ end
+
+ def test_truncate_should_not_sanitize_safe_input
+ assert_equal "Hello code!World!".html_safe, :length => 12)
+ assert_equal "Hello code!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 beautiful 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 beautiful morning",
+ highlight("This is a beautiful morning", "beautiful")
+ )
+ end
+
+ def test_highlight_should_not_sanitize_input_if_safe_option
+ assert_equal(
+ "This is a beautiful morning",
+ highlight("This is a beautiful morning", "beautiful", :safe => true)
+ )
+ end
+
+ def test_highlight_should_not_sanitize_safe_input
+ assert_equal(
+ "This is a beautiful morning",
+ highlight("This is a beautiful morning".html_safe, "beautiful")
+ )
+ end
+
def test_highlight_with_regexp
assert_equal(
"This is a beautiful! morning",
@@ -163,7 +211,7 @@ class TextHelperTest < ActionView::TestCase
highlight("This is a beautiful morning, but also a beautiful day
", "beautiful")
)
assert_equal(
- "This is a beautiful morning, but also a beautiful day
",
+ "This is a beautiful morning, but also a beautiful day
",
highlight("This is a beautiful morning, but also a beautiful day
", "beautiful")
)
end
@@ -286,7 +334,17 @@ class TextHelperTest < ActionView::TestCase
%{#{CGI::escapeHTML link_text}}
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 = %{#{email_raw}}
link_raw = 'http://www.rubyonrails.com'
@@ -378,6 +436,21 @@ class TextHelperTest < ActionView::TestCase
assert_equal %(#{link10_result} Link
), auto_link("#{link10_raw} Link
")
end
+ def test_auto_link_should_sanitize_unsafe_input
+ link_raw = %{http://www.rubyonrails.com?id=1&num=2}
+ assert_equal %{http://www.rubyonrails.com?id=1&num=2}, 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 %{http://www.rubyonrails.com?id=1&num=2}, 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 %{http://www.rubyonrails.com?id=1&num=2}, 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("This is Textile! Rejoice!
", textilize("*This is Textile!* Rejoice!"))
end
@@ -600,6 +678,18 @@ class TextHelperTest < ActionView::TestCase
assert_equal("This is worded <strong>strongly</strong>
", textilize("This is worded strongly", :filter_html))
end
+ def test_textilize_should_sanitize_unsafe_input
+ assert_equal("This is worded strongly
", textilize("This is worded strongly"))
+ end
+
+ def test_textilize_should_not_sanitize_input_if_safe_option
+ assert_equal("This is worded strongly
", textilize("This is worded strongly", :safe))
+ end
+
+ def test_textilize_should_not_sanitize_safe_input
+ assert_equal("This is worded strongly
", textilize("This is worded strongly".html_safe))
+ end
+
def test_textilize_with_hard_breaks
assert_equal("This is one scary world.
\n True.
", textilize("This is one scary world.\n True."))
end
--
cgit v1.2.3