From 08b029cd245a304fd204081c1c7f2a96ce32f4c6 Mon Sep 17 00:00:00 2001 From: Sergey Nartimov Date: Mon, 20 Feb 2012 15:19:28 -0800 Subject: [PATCH 1/5] fix output safety issue with select options --- actionpack/lib/action_view/helpers/tags/base.rb | 6 +++--- actionpack/test/template/form_options_helper_test.rb | 9 ++++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_view/helpers/tags/base.rb b/actionpack/lib/action_view/helpers/tags/base.rb index 1ece0ad2fcc67..b3105ade621ef 100644 --- a/actionpack/lib/action_view/helpers/tags/base.rb +++ b/actionpack/lib/action_view/helpers/tags/base.rb @@ -133,13 +133,13 @@ def select_content_tag(option_tags, options, html_options) def add_options(option_tags, options, value = nil) if options[:include_blank] - option_tags = "\n" + option_tags + option_tags = content_tag('option', options[:include_blank].kind_of?(String) ? options[:include_blank] : nil, :value => '') + "\n" + option_tags end if value.blank? && options[:prompt] prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('helpers.select.prompt', :default => 'Please select') - option_tags = "\n" + option_tags + option_tags = content_tag('option', prompt, :value => '') + "\n" + option_tags end - option_tags.html_safe + option_tags end end end diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb index 6b8d62df62d18..606d454cb374b 100644 --- a/actionpack/test/template/form_options_helper_test.rb +++ b/actionpack/test/template/form_options_helper_test.rb @@ -509,7 +509,7 @@ def @post.to_param; 108; end def test_select_under_fields_for_with_string_and_given_prompt @post = Post.new - options = "" + options = "".html_safe output_buffer = fields_for :post, @post do |f| concat f.select(:category, options, :prompt => 'The prompt') @@ -665,6 +665,13 @@ def test_select_with_index_option ) end + def test_select_escapes_options + assert_dom_equal( + '', + select('post', 'title', '') + ) + end + def test_select_with_selected_nil @post = Post.new @post.category = "" From 71b95bd954edd8e8f09293044d49b34b9120279a Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Mon, 13 Feb 2012 17:54:58 +0900 Subject: [PATCH 2/5] add AS::SafeBuffer#clone_empty --- .../lib/active_support/core_ext/string/output_safety.rb | 6 ++++++ activesupport/test/safe_buffer_test.rb | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index dd780da157869..20c2befc5786e 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -124,6 +124,12 @@ def initialize_copy(other) @dirty = other.dirty? end + def clone_empty + new_safe_buffer = self[0, 0] + new_safe_buffer.instance_variable_set(:@dirty, @dirty) + new_safe_buffer + end + def concat(value) if dirty? || value.html_safe? super(value) diff --git a/activesupport/test/safe_buffer_test.rb b/activesupport/test/safe_buffer_test.rb index bdde5141a98ad..ff36af5c40b36 100644 --- a/activesupport/test/safe_buffer_test.rb +++ b/activesupport/test/safe_buffer_test.rb @@ -118,4 +118,13 @@ def test_titleize assert_not_nil dirty assert !dirty end + + test "clone_empty returns an empty buffer" do + assert_equal '', ActiveSupport::SafeBuffer.new('foo').clone_empty + end + + test "clone_empty keeps the original dirtyness" do + assert @buffer.clone_empty.html_safe? + assert !@buffer.gsub!('', '').clone_empty.html_safe? + end end From dcf0964d64903605c6dc9d2e149ca0e45bd2aea2 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Mon, 13 Feb 2012 17:57:05 +0900 Subject: [PATCH 3/5] use AS::SafeBuffer#clone_empty for flushing the output_buffer --- actionpack/lib/action_view/helpers/capture_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index 17bbfe2efd9f0..278139cadb67f 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -215,7 +215,7 @@ def with_output_buffer(buf = nil) #:nodoc: def flush_output_buffer #:nodoc: if output_buffer && !output_buffer.empty? response.body_parts << output_buffer - self.output_buffer = output_buffer[0,0] + self.output_buffer = output_buffer.respond_to?(:clone_empty) ? output_buffer.clone_empty : output_buffer[0, 0] nil end end From 71d8c77e5ac74c2aa20eff6b3d6a8b8ca24114d7 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Mon, 13 Feb 2012 17:58:01 +0900 Subject: [PATCH 4/5] delete vulnerable AS::SafeBuffer#[] --- .../lib/active_support/core_ext/string/output_safety.rb | 6 ------ activesupport/test/safe_buffer_test.rb | 6 ------ 2 files changed, 12 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index 20c2befc5786e..7f158369f43de 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -103,12 +103,6 @@ def initialize end end - def[](*args) - new_safe_buffer = super - new_safe_buffer.instance_eval { @dirty = false } - new_safe_buffer - end - def safe_concat(value) raise SafeConcatError if dirty? original_concat(value) diff --git a/activesupport/test/safe_buffer_test.rb b/activesupport/test/safe_buffer_test.rb index ff36af5c40b36..56f9a9a51ad91 100644 --- a/activesupport/test/safe_buffer_test.rb +++ b/activesupport/test/safe_buffer_test.rb @@ -113,12 +113,6 @@ def test_titleize assert_kind_of NilClass, @buffer.slice("chipchop") end - test "Should initialize @dirty to false for new instance when sliced" do - dirty = @buffer[0,0].send(:dirty?) - assert_not_nil dirty - assert !dirty - end - test "clone_empty returns an empty buffer" do assert_equal '', ActiveSupport::SafeBuffer.new('foo').clone_empty end From 8ccaa34103f1c37f7549f7f6c47a21dba21624db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 29 Feb 2012 22:30:51 +0100 Subject: [PATCH 5/5] Ensure [] respects the status of the buffer. --- .../core_ext/string/output_safety.rb | 30 +++++++++++-------- activesupport/test/safe_buffer_test.rb | 27 ++++++++++++++--- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index 7f158369f43de..728ab087fa44a 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -103,19 +103,31 @@ def initialize end end + def [](*args) + return super if args.size < 2 + + if html_safe? + new_safe_buffer = super + new_safe_buffer.instance_eval { @html_safe = true } + new_safe_buffer + else + to_str[*args] + end + end + def safe_concat(value) - raise SafeConcatError if dirty? + raise SafeConcatError unless html_safe? original_concat(value) end def initialize(*) - @dirty = false + @html_safe = true super end def initialize_copy(other) super - @dirty = other.dirty? + @html_safe = other.html_safe? end def clone_empty @@ -125,7 +137,7 @@ def clone_empty end def concat(value) - if dirty? || value.html_safe? + if !html_safe? || value.html_safe? super(value) else super(ERB::Util.h(value)) @@ -138,7 +150,7 @@ def +(other) end def html_safe? - !dirty? + defined?(@html_safe) && @html_safe end def to_s @@ -161,18 +173,12 @@ def #{unsafe_method}(*args, &block) # def capitalize(*args, &block) end # end def #{unsafe_method}!(*args) # def capitalize!(*args) - @dirty = true # @dirty = true + @html_safe = false # @html_safe = false super # super end # end EOT end end - - protected - - def dirty? - @dirty - end end end diff --git a/activesupport/test/safe_buffer_test.rb b/activesupport/test/safe_buffer_test.rb index 56f9a9a51ad91..047b89be2a717 100644 --- a/activesupport/test/safe_buffer_test.rb +++ b/activesupport/test/safe_buffer_test.rb @@ -84,13 +84,13 @@ def test_titleize assert_equal "hello<>", clean + @buffer end - test "Should concat as a normal string when dirty" do + test "Should concat as a normal string when safe" do clean = "hello".html_safe @buffer.gsub!('', '<>') assert_equal "<>hello", @buffer + clean end - test "Should preserve dirty? status on copy" do + test "Should preserve html_safe? status on copy" do @buffer.gsub!('', '<>') assert !@buffer.dup.html_safe? end @@ -102,14 +102,14 @@ def test_titleize assert_equal "') + + # calling gsub! makes the dirty flag true + assert !x.html_safe?, "should not be safe" + + # getting a slice of it + y = x[0..-1] + + # should still be unsafe + assert !y.html_safe?, "should not be safe" + end end