alexyorke
alexyorke

Reputation: 4319

Don't escape html in ruby on rails

rails 3 seems to escape everything, including html. I have tried using raw() but it still escapes html. Is there a workaround? This is my helper that I am using (/helpers/application_helper.rb):

module ApplicationHelper
  def good_time(status = true)
    res = ""
    if status == true
      res << "Status is true, with a long message attached..."
    else
      res << "Status is false, with another long message"
    end
  end
end

I am calling the helper in my view using this code:

<%= raw(good_time(true)) %>

Upvotes: 53

Views: 33000

Answers (2)

DreadPirateShawn
DreadPirateShawn

Reputation: 8420

I ran into this same thing and discovered a safer solution than using html_safe, especially once you introduce strings which are dynamic.

First, the updated code:

def good_time(long_message1, long_message2, status = true)
  html = "".html_safe
  html << "Status is #{status}, "
  if status
    html << long_message1
  else
    html << long_message2
  end
  html
end

<%= good_time(true) %>

This escapes long_message content if it is unsafe, but leaves it unescaped if it is safe.

This allows "long message for success & such." to display properly, but also escapes "malicious message <script>alert('foo')</script>".

The explanation boils down to this -- 'foo'.html_safe returns an ActiveSupport::SafeBuffer which acts like a String in every way except one: When you append a String to a SafeBuffer (by calling + or <<), that other String is HTML-escaped before it is appended to the SafeBuffer. When you append another SafeBuffer to a SafeBuffer, no escaping will occur. Rails is rendering all of your views under the hood using SafeBuffers, so the updated method above ends up providing Rails with a SafeBuffer that we've controlled to perform escaping on the long_message "as-needed" rather than "always".

Now, the credit for this answer goes entirely to Henning Koch, and is explained in far more detail at Everything you know about html_safe is wrong -- my recap above attempts only to provide the essence of the explanation in the event that this link ever dies.

Upvotes: 5

Mischa
Mischa

Reputation: 43318

You can use .html_safe like this:

def good_time(status = true)
  if status
    "Status is true, with a long message attached...".html_safe
  else
    "Status is false, with another long message".html_safe
  end
end

<%= good_time(true) %>

Upvotes: 99

Related Questions