Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why URI.escape fails when called on ActionView::OutputBuffer?

I'm upgrading an application from Rails 2 to Rails 3. Apparently, calling render() now returns ActionView::OutputBuffer and not String. I need to pass the results of render() to URI.escape(), and this fails with exception...

Here is my brief testing in the console

ob = ActionView::OutputBuffer.new("test test")
URI.escape(ob)
    `NoMethodError: undefined method 'each_byte' for nil:NilClass`. 
        from /opt/ruby19/lib/ruby/1.9.1/uri/common.rb:307:in `block in escape'
        from ..../ruby/1.9.1/gems/activesupport-3.2.1/lib/active_support/core_ext/string/output_safety.rb:160:in `gsub'
        from ..../ruby/1.9.1/gems/activesupport-3.2.1/lib/active_support/core_ext/string/output_safety.rb:160:in `gsub'
        from /opt/ruby19/lib/ruby/1.9.1/uri/common.rb:304:in `escape'
        from /opt/ruby19/lib/ruby/1.9.1/uri/common.rb:623:in `escape'

Moreover, calling to_s on OutputBuffer returns same OutputBuffer class, so I cannot even convert this buffer into a honest string?

ob.to_s.class 
    ActionView::OutputBuffer

Of course, calling URI.escape("test test") returns "test%20test" as expected, so this is not URI problem.

Environment:

  • ruby 1.9.3p125 (2012-02-16 revision 34643) [i686-linux]
  • Rails 3.2.1

My question is: Why does this happen and how can I work around this issue?

Update: Apparently, using '' + ob as a form of ob.to_s converts OutputBuffer to String, which effectively works around the problem... But my question 'why does this happen' still remains, e.g. is this a bug, should I report it, or I'm doing something wrong?

like image 769
haimg Avatar asked Feb 27 '12 18:02

haimg


2 Answers

This is a bug in Rails:

When calling gsub with a block on an ActiveSupport::SafeBuffer the global variables $1, $2, etc. for referencing submatches are not always properly set (anymore?) when the block is called.

This is why URI.escape (and any other function that uses gsub() will fail on ActiveSupprt::Safebuffer.

There are several discussions about this, apparently the safest route right now is to call to_str before passing SafeBuffer to anything that can call gsub, e.g. URI.encode, escape_javascript and similar functions.

My other quesion about to_s returning the same class - obviously safe buffer will return itself and not a bare String, this is by design. In order to get a true String, .to_str can be used.

like image 59
haimg Avatar answered Oct 16 '22 08:10

haimg


This is due to the fact that Rails 3 introduced the concept of safe buffers

In Rails3 your Views are protected by XSS by default by making all rendering be safely escaped unless you explicitly use the raw() helper or html_safe

like image 1
Tigraine Avatar answered Oct 16 '22 09:10

Tigraine